diff --git a/packages/ui/MIGRATION_STATUS.md b/packages/ui/MIGRATION_STATUS.md new file mode 100644 index 0000000000..52ee2b6fea --- /dev/null +++ b/packages/ui/MIGRATION_STATUS.md @@ -0,0 +1,104 @@ +# UI 组件库迁移状态 + +## 使用示例 + +```typescript +// 从 @cherrystudio/ui 导入组件 +import { Spinner, DividerWithText, InfoTooltip } from '@cherrystudio/ui' + +// 在组件中使用 +function MyComponent() { + return ( +
+ + + +
+ ) +} +``` + +## 目录结构说明 + +``` +@packages/ui/ +├── src/ +│ ├── components/ # 组件主目录 +│ │ ├── base/ # 基础组件(按钮、输入框、标签等) +│ │ ├── display/ # 显示组件(卡片、列表、表格等) +│ │ ├── layout/ # 布局组件(容器、网格、间距等) +│ │ ├── icons/ # 图标组件 +│ │ ├── interactive/ # 交互组件(弹窗、提示、下拉等) +│ │ └── composite/ # 复合组件(多个基础组件组合而成) +│ ├── hooks/ # 自定义 React Hooks +│ └── types/ # TypeScript 类型定义 +``` + +### 组件分类指南 + +提交 PR 时,请根据组件功能将其放入正确的目录: + +- **base**: 最基础的 UI 元素,如按钮、输入框、开关、标签等 +- **display**: 用于展示内容的组件,如卡片、列表、表格、标签页等 +- **layout**: 用于页面布局的组件,如容器、网格系统、分隔符等 +- **icons**: 所有图标相关的组件 +- **interactive**: 需要用户交互的组件,如模态框、抽屉、提示框、下拉菜单等 +- **composite**: 复合组件,由多个基础组件组合而成 + +## 迁移概览 + +- **总组件数**: 236 +- **已迁移**: 18 +- **已重构**: 0 +- **待迁移**: 218 + +## 组件状态表 + +| 组件名称 | 原路径 | 分类 | 迁移状态 | 重构状态 | +|---------|--------|------|---------|---------| +| CopyButton | src/renderer/src/components/CopyButton.tsx | base | ✅ | ❌ | +| DividerWithText | src/renderer/src/components/DividerWithText.tsx | base | ✅ | ❌ | +| EmojiIcon | src/renderer/src/components/EmojiIcon.tsx | base | ✅ | ❌ | +| IndicatorLight | src/renderer/src/components/IndicatorLight.tsx | base | ✅ | ❌ | +| Spinner | src/renderer/src/components/Spinner.tsx | base | ✅ | ❌ | +| TextBadge | src/renderer/src/components/TextBadge.tsx | base | ✅ | ❌ | +| Ellipsis | src/renderer/src/components/Ellipsis/index.tsx | display | ✅ | ❌ | +| ExpandableText | src/renderer/src/components/ExpandableText.tsx | display | ✅ | ❌ | +| ThinkingEffect | src/renderer/src/components/ThinkingEffect.tsx | display | ✅ | ❌ | +| HorizontalScrollContainer | src/renderer/src/components/HorizontalScrollContainer/index.tsx | layout | ✅ | ❌ | +| Scrollbar | src/renderer/src/components/Scrollbar/index.tsx | layout | ✅ | ❌ | +| VisionIcon | src/renderer/src/components/Icons/VisionIcon.tsx | icons | ✅ | ❌ | +| WebSearchIcon | src/renderer/src/components/Icons/WebSearchIcon.tsx | icons | ✅ | ❌ | +| ToolsCallingIcon | src/renderer/src/components/Icons/ToolsCallingIcon.tsx | icons | ✅ | ❌ | +| FileIcons | src/renderer/src/components/Icons/FileIcons.tsx | icons | ✅ | ❌ | +| SvgSpinners180Ring | src/renderer/src/components/Icons/SvgSpinners180Ring.tsx | icons | ✅ | ❌ | +| ReasoningIcon | src/renderer/src/components/Icons/ReasoningIcon.tsx | icons | ✅ | ❌ | +| InfoTooltip | src/renderer/src/components/TooltipIcons/InfoTooltip.tsx | interactive | ✅ | ❌ | + +## 迁移步骤 + +### 第一阶段:复制迁移(当前阶段) +- 将组件原样复制到 @packages/ui +- 保留原有依赖(antd、styled-components 等) +- 在文件顶部添加原路径注释 + +### 第二阶段:重构优化 +- 移除 antd 依赖,替换为 HeroUI +- 移除 styled-components,替换为 Tailwind CSS +- 优化组件 API 和类型定义 + +## 注意事项 + +1. **不迁移**包含以下依赖的组件**(解耦后可迁移)**: + - window.api 调用 + - Redux(useSelector、useDispatch 等) + - 其他外部数据源 + +2. **可迁移**但需要后续解耦的组件: + - 使用 i18n 的组件(将 i18n 改为 props 传入) + - 使用 antd 的组件(后续替换为 HeroUI) + +3. **提交规范**: + - 每次 PR 专注于一个类别的组件 + - 确保所有迁移的组件都有导出 + - 更新此文档的迁移状态 \ No newline at end of file diff --git a/packages/ui/MIGRATION_STATUS_EN.md b/packages/ui/MIGRATION_STATUS_EN.md new file mode 100644 index 0000000000..fd5879e2ff --- /dev/null +++ b/packages/ui/MIGRATION_STATUS_EN.md @@ -0,0 +1,106 @@ +# UI Component Library Migration Status + +## Usage Example + +```typescript +// Import components from @cherrystudio/ui +import { Spinner, DividerWithText, InfoTooltip } from '@cherrystudio/ui' + +// Use in components +function MyComponent() { + return ( +
+ + + +
+ ) +} +``` + +## Directory Structure + +``` +@packages/ui/ +├── src/ +│ ├── components/ # Main components directory +│ │ ├── base/ # Basic components (buttons, inputs, labels, etc.) +│ │ ├── display/ # Display components (cards, lists, tables, etc.) +│ │ ├── layout/ # Layout components (containers, grids, spacing, etc.) +│ │ ├── icons/ # Icon components +│ │ ├── interactive/ # Interactive components (modals, tooltips, dropdowns, etc.) +│ │ └── composite/ # Composite components (made from multiple base components) +│ ├── hooks/ # Custom React Hooks +│ └── types/ # TypeScript type definitions +``` + +### Component Classification Guide + +When submitting PRs, please place components in the correct directory based on their function: + +- **base**: Most basic UI elements like buttons, inputs, switches, labels, etc. +- **display**: Components for displaying content like cards, lists, tables, tabs, etc. +- **layout**: Components for page layout like containers, grid systems, dividers, etc. +- **icons**: All icon-related components +- **interactive**: Components requiring user interaction like modals, drawers, tooltips, dropdowns, etc. +- **composite**: Composite components made from multiple base components + +## Migration Overview + +- **Total Components**: 236 +- **Migrated**: 18 +- **Refactored**: 0 +- **Pending Migration**: 218 + +## Component Status Table + +| Component Name | Original Path | Category | Migration Status | Refactoring Status | +|---------------|---------------|----------|------------------|-------------------| +| CopyButton | src/renderer/src/components/CopyButton.tsx | base | ✅ | ❌ | +| DividerWithText | src/renderer/src/components/DividerWithText.tsx | base | ✅ | ❌ | +| EmojiIcon | src/renderer/src/components/EmojiIcon.tsx | base | ✅ | ❌ | +| IndicatorLight | src/renderer/src/components/IndicatorLight.tsx | base | ✅ | ❌ | +| Spinner | src/renderer/src/components/Spinner.tsx | base | ✅ | ❌ | +| TextBadge | src/renderer/src/components/TextBadge.tsx | base | ✅ | ❌ | +| Ellipsis | src/renderer/src/components/Ellipsis/index.tsx | display | ✅ | ❌ | +| ExpandableText | src/renderer/src/components/ExpandableText.tsx | display | ✅ | ❌ | +| ThinkingEffect | src/renderer/src/components/ThinkingEffect.tsx | display | ✅ | ❌ | +| HorizontalScrollContainer | src/renderer/src/components/HorizontalScrollContainer/index.tsx | layout | ✅ | ❌ | +| Scrollbar | src/renderer/src/components/Scrollbar/index.tsx | layout | ✅ | ❌ | +| VisionIcon | src/renderer/src/components/Icons/VisionIcon.tsx | icons | ✅ | ❌ | +| WebSearchIcon | src/renderer/src/components/Icons/WebSearchIcon.tsx | icons | ✅ | ❌ | +| ToolsCallingIcon | src/renderer/src/components/Icons/ToolsCallingIcon.tsx | icons | ✅ | ❌ | +| FileIcons | src/renderer/src/components/Icons/FileIcons.tsx | icons | ✅ | ❌ | +| SvgSpinners180Ring | src/renderer/src/components/Icons/SvgSpinners180Ring.tsx | icons | ✅ | ❌ | +| ReasoningIcon | src/renderer/src/components/Icons/ReasoningIcon.tsx | icons | ✅ | ❌ | +| InfoTooltip | src/renderer/src/components/TooltipIcons/InfoTooltip.tsx | interactive | ✅ | ❌ | + +## Migration Steps + +### Phase 1: Copy Migration (Current Phase) + +- Copy components as-is to @packages/ui +- Retain original dependencies (antd, styled-components, etc.) +- Add original path comment at file top + +### Phase 2: Refactor and Optimize + +- Remove antd dependencies, replace with HeroUI +- Remove styled-components, replace with Tailwind CSS +- Optimize component APIs and type definitions + +## Notes + +1. **Do NOT migrate** components with these dependencies: + - window.api calls + - Redux (useSelector, useDispatch, etc.) + - Other external data sources + +2. **Can migrate** but need decoupling later: + - Components using i18n (change i18n to props) + - Components using antd (replace with HeroUI later) + +3. **Submission Guidelines**: + - Each PR should focus on one category of components + - Ensure all migrated components are exported + - Update migration status in this document \ No newline at end of file diff --git a/packages/ui/src/components/base/CopyButton/index.tsx b/packages/ui/src/components/base/CopyButton/index.tsx new file mode 100644 index 0000000000..51e47cfc28 --- /dev/null +++ b/packages/ui/src/components/base/CopyButton/index.tsx @@ -0,0 +1,69 @@ +// Original path: src/renderer/src/components/CopyButton.tsx +import { Tooltip } from 'antd' +import { Copy } from 'lucide-react' +import { FC } from 'react' +import styled from 'styled-components' + +interface CopyButtonProps { + tooltip?: string + label?: string + color?: string + hoverColor?: string + size?: number +} + +interface ButtonContainerProps { + $color: string + $hoverColor: string +} + +const CopyButton: FC = ({ + tooltip, + label, + color = 'var(--color-text-2)', + hoverColor = 'var(--color-primary)', + size = 14, + ...props +}) => { + const button = ( + + + {label && {label}} + + ) + + if (tooltip) { + return {button} + } + + return button +} + +const ButtonContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + gap: 4px; + cursor: pointer; + color: ${(props) => props.$color}; + transition: color 0.2s; + + .copy-icon { + color: ${(props) => props.$color}; + transition: color 0.2s; + } + + &:hover { + color: ${(props) => props.$hoverColor}; + + .copy-icon { + color: ${(props) => props.$hoverColor}; + } + } +` + +const RightText = styled.span<{ size: number }>` + font-size: ${(props) => props.size}px; +` + +export default CopyButton diff --git a/packages/ui/src/components/base/DividerWithText.tsx b/packages/ui/src/components/base/DividerWithText/index.tsx similarity index 100% rename from packages/ui/src/components/base/DividerWithText.tsx rename to packages/ui/src/components/base/DividerWithText/index.tsx diff --git a/packages/ui/src/components/base/EmojiIcon/index.tsx b/packages/ui/src/components/base/EmojiIcon/index.tsx new file mode 100644 index 0000000000..f20860bec3 --- /dev/null +++ b/packages/ui/src/components/base/EmojiIcon/index.tsx @@ -0,0 +1,49 @@ +// Original path: src/renderer/src/components/EmojiIcon.tsx +import { FC } from 'react' +import styled from 'styled-components' + +interface EmojiIconProps { + emoji: string + className?: string + size?: number + fontSize?: number +} + +const EmojiIcon: FC = ({ emoji, className, size = 26, fontSize = 15 }) => { + return ( + + {emoji || '⭐️'} + {emoji} + + ) +} + +const Container = styled.div<{ $size: number; $fontSize: number }>` + width: ${({ $size }) => $size}px; + height: ${({ $size }) => $size}px; + border-radius: ${({ $size }) => $size / 2}px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + font-size: ${({ $fontSize }) => $fontSize}px; + position: relative; + overflow: hidden; + margin-right: 3px; +` + +const EmojiBackground = styled.div` + width: 100%; + height: 100%; + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 200%; + transform: scale(1.5); + filter: blur(5px); + opacity: 0.4; +` + +export default EmojiIcon diff --git a/packages/ui/src/components/base/IndicatorLight.tsx b/packages/ui/src/components/base/IndicatorLight/index.tsx similarity index 100% rename from packages/ui/src/components/base/IndicatorLight.tsx rename to packages/ui/src/components/base/IndicatorLight/index.tsx diff --git a/packages/ui/src/components/base/Spinner.tsx b/packages/ui/src/components/base/Spinner/index.tsx similarity index 100% rename from packages/ui/src/components/base/Spinner.tsx rename to packages/ui/src/components/base/Spinner/index.tsx diff --git a/packages/ui/src/components/base/TextBadge.tsx b/packages/ui/src/components/base/TextBadge/index.tsx similarity index 100% rename from packages/ui/src/components/base/TextBadge.tsx rename to packages/ui/src/components/base/TextBadge/index.tsx diff --git a/packages/ui/src/components/display/Ellipsis.tsx b/packages/ui/src/components/display/Ellipsis/index.tsx similarity index 100% rename from packages/ui/src/components/display/Ellipsis.tsx rename to packages/ui/src/components/display/Ellipsis/index.tsx diff --git a/packages/ui/src/components/display/ExpandableText.tsx b/packages/ui/src/components/display/ExpandableText/index.tsx similarity index 100% rename from packages/ui/src/components/display/ExpandableText.tsx rename to packages/ui/src/components/display/ExpandableText/index.tsx diff --git a/packages/ui/src/components/display/ThinkingEffect/defaultVariants.ts b/packages/ui/src/components/display/ThinkingEffect/defaultVariants.ts new file mode 100644 index 0000000000..73afaa6342 --- /dev/null +++ b/packages/ui/src/components/display/ThinkingEffect/defaultVariants.ts @@ -0,0 +1,38 @@ +import type { Variants } from 'motion/react' +export const lightbulbVariants: Variants = { + active: { + opacity: [1, 0.2, 1], + transition: { + duration: 1.2, + ease: 'easeInOut', + times: [0, 0.5, 1], + repeat: Infinity + } + }, + idle: { + opacity: 1, + transition: { + duration: 0.3, + ease: 'easeInOut' + } + } +} + +export const lightbulbSoftVariants: Variants = { + active: { + opacity: [1, 0.5, 1], + transition: { + duration: 2, + ease: 'easeInOut', + times: [0, 0.5, 1], + repeat: Infinity + } + }, + idle: { + opacity: 1, + transition: { + duration: 0.3, + ease: 'easeInOut' + } + } +} diff --git a/packages/ui/src/components/display/ThinkingEffect/index.tsx b/packages/ui/src/components/display/ThinkingEffect/index.tsx new file mode 100644 index 0000000000..fca5e1bd8a --- /dev/null +++ b/packages/ui/src/components/display/ThinkingEffect/index.tsx @@ -0,0 +1,190 @@ +// Original path: src/renderer/src/components/ThinkingEffect.tsx +import { isEqual } from 'lodash' +import { ChevronRight, Lightbulb } from 'lucide-react' +import { motion } from 'motion/react' +import React, { useEffect, useMemo, useState } from 'react' +import styled from 'styled-components' + +import { lightbulbVariants } from './defaultVariants' + +interface Props { + isThinking: boolean + thinkingTimeText: React.ReactNode + content: string + expanded: boolean +} + +const ThinkingEffect: React.FC = ({ isThinking, thinkingTimeText, content, expanded }) => { + const [messages, setMessages] = useState([]) + + useEffect(() => { + const allLines = (content || '').split('\n') + const newMessages = isThinking ? allLines.slice(0, -1) : allLines + const validMessages = newMessages.filter((line) => line.trim() !== '') + + if (!isEqual(messages, validMessages)) { + setMessages(validMessages) + } + }, [content, isThinking, messages]) + + const showThinking = useMemo(() => { + return isThinking && !expanded + }, [expanded, isThinking]) + + const LINE_HEIGHT = 14 + + const containerHeight = useMemo(() => { + if (!showThinking || messages.length < 1) return 38 + return Math.min(75, Math.max(messages.length + 1, 2) * LINE_HEIGHT + 25) + }, [showThinking, messages.length]) + + return ( + + + + + + + + + {thinkingTimeText} + + {showThinking && ( + + + {messages.map((message, index) => { + if (index < messages.length - 5) return null + + return {message} + })} + + + )} + + + + + + ) +} + +const ThinkingContainer = styled.div` + width: 100%; + border-radius: 10px; + overflow: hidden; + position: relative; + display: flex; + align-items: center; + border: 0.5px solid var(--color-border); + transition: height, border-radius, 150ms; + pointer-events: none; + user-select: none; + &.expanded { + border-radius: 10px 10px 0 0; + } +` + +const Title = styled.div` + position: absolute; + inset: 0 0 auto 0; + font-size: 14px; + line-height: 14px; + font-weight: 500; + padding: 10px 0; + z-index: 99; + transition: padding-top 150ms; + &.showThinking { + padding-top: 12px; + } +` + +const LoadingContainer = styled.div` + width: 50px; + display: flex; + justify-content: center; + align-items: center; + height: 100%; + flex-shrink: 0; + position: relative; + padding-left: 5px; + transition: width 150ms; + > div { + display: flex; + justify-content: center; + align-items: center; + } +` + +const TextContainer = styled.div` + flex: 1; + height: 100%; + padding: 5px 0; + overflow: hidden; + position: relative; +` + +const Content = styled.div` + width: 100%; + height: 100%; + mask: linear-gradient( + to bottom, + rgb(0 0 0 / 0%) 0%, + rgb(0 0 0 / 0%) 35%, + rgb(0 0 0 / 25%) 40%, + rgb(0 0 0 / 100%) 90%, + rgb(0 0 0 / 100%) 100% + ); + position: relative; +` + +const Messages = styled(motion.div)` + width: 100%; + position: absolute; + top: 100%; + display: flex; + flex-direction: column; + justify-content: flex-end; +` + +const Message = styled.div` + width: 100%; + line-height: 14px; + font-size: 11px; + color: var(--color-text-2); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +` + +const ArrowContainer = styled.div` + width: 40px; + display: flex; + justify-content: center; + align-items: center; + height: 100%; + flex-shrink: 0; + position: relative; + color: var(--color-border); + transition: transform 150ms; + &.expanded { + transform: rotate(90deg); + } +` + +export default ThinkingEffect diff --git a/packages/ui/src/components/icons/FileIcons/index.tsx b/packages/ui/src/components/icons/FileIcons/index.tsx new file mode 100644 index 0000000000..dd5885e172 --- /dev/null +++ b/packages/ui/src/components/icons/FileIcons/index.tsx @@ -0,0 +1,71 @@ +// Original path: src/renderer/src/components/Icons/FileIcons.tsx +import { CSSProperties, SVGProps } from 'react' + +interface BaseFileIconProps extends SVGProps { + size?: string | number + text?: string +} + +const textStyle: CSSProperties = { + fontStyle: 'italic', + fontSize: '7.70985px', + lineHeight: 0.8, + fontFamily: "'Times New Roman'", + textAlign: 'center', + writingMode: 'horizontal-tb', + direction: 'ltr', + textAnchor: 'middle', + fill: 'none', + stroke: '#000000', + strokeWidth: '0.289119', + strokeLinejoin: 'round', + strokeDasharray: 'none' +} + +const tspanStyle: CSSProperties = { + fontStyle: 'normal', + fontVariant: 'normal', + fontWeight: 'normal', + fontStretch: 'condensed', + fontSize: '7.70985px', + lineHeight: 0.8, + fontFamily: 'Arial', + fill: '#000000', + fillOpacity: 1, + strokeWidth: '0.289119', + strokeDasharray: 'none' +} + +const BaseFileIcon = ({ size = '1.1em', text = 'SVG', ...props }: BaseFileIconProps) => ( + + + + + + + {text} + + + +) + +export const FileSvgIcon = (props: Omit) => +export const FilePngIcon = (props: Omit) => diff --git a/packages/ui/src/components/icons/ReasoningIcon/index.tsx b/packages/ui/src/components/icons/ReasoningIcon/index.tsx new file mode 100644 index 0000000000..5bbd296f2f --- /dev/null +++ b/packages/ui/src/components/icons/ReasoningIcon/index.tsx @@ -0,0 +1,31 @@ +// Original path: src/renderer/src/components/Icons/ReasoningIcon.tsx +import { Tooltip } from 'antd' +import React, { FC } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +const ReasoningIcon: FC, HTMLElement>> = (props) => { + const { t } = useTranslation() + + return ( + + + + + + ) +} + +const Container = styled.div` + display: flex; + justify-content: center; + align-items: center; +` + +const Icon = styled.i` + color: var(--color-link); + font-size: 16px; + margin-right: 6px; +` + +export default ReasoningIcon diff --git a/packages/ui/src/components/icons/SvgSpinners180Ring/index.tsx b/packages/ui/src/components/icons/SvgSpinners180Ring/index.tsx new file mode 100644 index 0000000000..c0b3abbfca --- /dev/null +++ b/packages/ui/src/components/icons/SvgSpinners180Ring/index.tsx @@ -0,0 +1,22 @@ +// Original path: src/renderer/src/components/Icons/SvgSpinners180Ring.tsx +import { SVGProps } from 'react' + +export function SvgSpinners180Ring(props: SVGProps & { size?: number | string }) { + const { size = '1em', ...svgProps } = props + + return ( + + {/* Icon from SVG Spinners by Utkarsh Verma - https://github.com/n3r4zzurr0/svg-spinners/blob/main/LICENSE */} + + + ) +} +export default SvgSpinners180Ring diff --git a/packages/ui/src/components/icons/ToolsCallingIcon.tsx b/packages/ui/src/components/icons/ToolsCallingIcon/index.tsx similarity index 100% rename from packages/ui/src/components/icons/ToolsCallingIcon.tsx rename to packages/ui/src/components/icons/ToolsCallingIcon/index.tsx diff --git a/packages/ui/src/components/icons/VisionIcon.tsx b/packages/ui/src/components/icons/VisionIcon/index.tsx similarity index 100% rename from packages/ui/src/components/icons/VisionIcon.tsx rename to packages/ui/src/components/icons/VisionIcon/index.tsx diff --git a/packages/ui/src/components/icons/WebSearchIcon.tsx b/packages/ui/src/components/icons/WebSearchIcon/index.tsx similarity index 100% rename from packages/ui/src/components/icons/WebSearchIcon.tsx rename to packages/ui/src/components/icons/WebSearchIcon/index.tsx diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 97104d11af..8b0f624dd4 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -1,5 +1,7 @@ // Base Components +export { default as CopyButton } from './base/CopyButton' export { default as DividerWithText } from './base/DividerWithText' +export { default as EmojiIcon } from './base/EmojiIcon' export { default as IndicatorLight } from './base/IndicatorLight' export { default as Spinner } from './base/Spinner' export { default as TextBadge } from './base/TextBadge' @@ -7,15 +9,22 @@ export { default as TextBadge } from './base/TextBadge' // Display Components export { default as Ellipsis } from './display/Ellipsis' export { default as ExpandableText } from './display/ExpandableText' +export { default as ThinkingEffect } from './display/ThinkingEffect' // Layout Components export { default as HorizontalScrollContainer } from './layout/HorizontalScrollContainer' export { default as Scrollbar } from './layout/Scrollbar' // Icon Components +export { FilePngIcon, FileSvgIcon } from './icons/FileIcons' +export { default as ReasoningIcon } from './icons/ReasoningIcon' +export { default as SvgSpinners180Ring } from './icons/SvgSpinners180Ring' export { default as ToolsCallingIcon } from './icons/ToolsCallingIcon' export { default as VisionIcon } from './icons/VisionIcon' export { default as WebSearchIcon } from './icons/WebSearchIcon' // Interactive Components export { default as InfoTooltip } from './interactive/InfoTooltip' + +// Composite Components (复合组件) +// 暂无复合组件 diff --git a/packages/ui/src/components/interactive/InfoTooltip.tsx b/packages/ui/src/components/interactive/InfoTooltip/index.tsx similarity index 95% rename from packages/ui/src/components/interactive/InfoTooltip.tsx rename to packages/ui/src/components/interactive/InfoTooltip/index.tsx index 53aa0c2a85..dee06bdd02 100644 --- a/packages/ui/src/components/interactive/InfoTooltip.tsx +++ b/packages/ui/src/components/interactive/InfoTooltip/index.tsx @@ -18,4 +18,4 @@ const InfoTooltip = ({ iconColor = 'var(--color-text-2)', iconSize = 14, iconSty ) } -export default InfoTooltip \ No newline at end of file +export default InfoTooltip diff --git a/packages/ui/src/components/layout/HorizontalScrollContainer.tsx b/packages/ui/src/components/layout/HorizontalScrollContainer/index.tsx similarity index 99% rename from packages/ui/src/components/layout/HorizontalScrollContainer.tsx rename to packages/ui/src/components/layout/HorizontalScrollContainer/index.tsx index 78b45481d6..1d1875c3a0 100644 --- a/packages/ui/src/components/layout/HorizontalScrollContainer.tsx +++ b/packages/ui/src/components/layout/HorizontalScrollContainer/index.tsx @@ -3,7 +3,7 @@ import { ChevronRight } from 'lucide-react' import { useEffect, useRef, useState } from 'react' import styled from 'styled-components' -import Scrollbar from './Scrollbar' +import Scrollbar from '../Scrollbar' /** * 水平滚动容器 diff --git a/packages/ui/src/components/layout/Scrollbar.tsx b/packages/ui/src/components/layout/Scrollbar/index.tsx similarity index 100% rename from packages/ui/src/components/layout/Scrollbar.tsx rename to packages/ui/src/components/layout/Scrollbar/index.tsx