diff --git a/eslint.config.mjs b/eslint.config.mjs
index 8eda2e6fa8..19b4efcd31 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -110,6 +110,26 @@ export default defineConfig([
'i18n/no-template-in-t': 'warn'
}
},
+ {
+ // Component Rules - prevent importing antd components when migration completed
+ files: ['src/**/*.{ts,tsx,js,jsx}'],
+ rules: {
+ 'no-restricted-imports': [
+ 'error',
+ {
+ paths: [
+ {
+ name: 'antd',
+ // TODO: migrate message again
+ importNames: ['Flex'],
+ message:
+ '❌ Do not import Flex from antd. Use our custom Layout components instead: import { Flex } from "@cherrystudio/ui"'
+ }
+ ]
+ }
+ ]
+ }
+ },
{
ignores: [
'node_modules/**',
diff --git a/packages/ui/MIGRATION_STATUS.md b/packages/ui/MIGRATION_STATUS.md
index 59c9ed0835..4206242b91 100644
--- a/packages/ui/MIGRATION_STATUS.md
+++ b/packages/ui/MIGRATION_STATUS.md
@@ -55,71 +55,71 @@ function MyComponent() {
## 组件状态表
-| Category | Component Name | Migration Status | Refactoring Status | Description |
-|----------|----------------|------------------|--------------------|-------------|
-| **base** | | | | 基础组件 |
-| | CopyButton | ✅ | ✅ | 复制按钮 |
-| | CustomTag | ✅ | ✅ | 自定义标签 |
-| | DividerWithText | ✅ | ✅ | 带文本的分隔线 |
-| | EmojiIcon | ✅ | ✅ | 表情图标 |
-| | ErrorBoundary | ✅ | ✅ | 错误边界 (通过 props 解耦) |
-| | StatusTag | ✅ | ✅ | 统一状态标签(合并了 ErrorTag、SuccessTag、WarnTag、InfoTag)|
-| | IndicatorLight | ✅ | ✅ | 指示灯 |
-| | Spinner | ✅ | ✅ | 加载动画 |
-| | TextBadge | ✅ | ✅ | 文本徽标 |
-| | CustomCollapse | ✅ | ✅ | 自定义折叠面板 |
-| **display** | | | | 显示组件 |
-| | Ellipsis | ✅ | ✅ | 文本省略 |
-| | ExpandableText | ✅ | ✅ | 可展开文本 |
-| | ThinkingEffect | ✅ | ✅ | 思考效果动画 |
-| | EmojiAvatar | ✅ | ✅ | 表情头像 |
-| | ListItem | ✅ | ✅ | 列表项 |
-| | MaxContextCount | ✅ | ✅ | 最大上下文数显示 |
-| | ProviderAvatar | ✅ | ✅ | 提供者头像 |
-| | CodeViewer | ❌ | ❌ | 代码查看器 (外部依赖) |
-| | OGCard | ❌ | ❌ | OG 卡片 |
-| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown 渲染器 |
-| | Preview/* | ❌ | ❌ | 预览组件 |
-| **layout** | | | | 布局组件 |
-| | HorizontalScrollContainer | ✅ | ❌ | 水平滚动容器 |
-| | Scrollbar | ✅ | ❌ | 滚动条 |
-| | Layout/* | ❌ | ❌ | 布局组件 |
-| | Tab/* | ❌ | ❌ | 标签页 (Redux 依赖) |
-| | TopView | ❌ | ❌ | 顶部视图 (window.api 依赖) |
-| **icons** | | | | 图标组件 |
-| | Icon | ✅ | ✅ | 图标工厂函数和预定义图标(合并了 CopyIcon、DeleteIcon、EditIcon、RefreshIcon、ResetIcon、ToolIcon、VisionIcon、WebSearchIcon、WrapIcon、UnWrapIcon、OcrIcon)|
-| | FileIcons | ✅ | ❌ | 文件图标 (FileSvgIcon、FilePngIcon) |
-| | ReasoningIcon | ✅ | ❌ | 推理图标 |
-| | SvgSpinners180Ring | ✅ | ❌ | 旋转加载图标 |
-| | ToolsCallingIcon | ✅ | ❌ | 工具调用图标 |
-| **interactive** | | | | 交互组件 |
-| | InfoTooltip | ✅ | ❌ | 信息提示 |
-| | HelpTooltip | ✅ | ❌ | 帮助提示 |
-| | WarnTooltip | ✅ | ❌ | 警告提示 |
-| | EditableNumber | ✅ | ❌ | 可编辑数字 |
-| | InfoPopover | ✅ | ❌ | 信息弹出框 |
-| | CollapsibleSearchBar | ✅ | ❌ | 可折叠搜索栏 |
-| | ImageToolButton | ✅ | ❌ | 图片工具按钮 |
-| | DraggableList | ✅ | ❌ | 可拖拽列表 |
-| | CodeEditor | ✅ | ❌ | 代码编辑器 |
-| | EmojiPicker | ❌ | ❌ | 表情选择器 (useTheme 依赖) |
-| | Selector | ✅ | ❌ | 选择器 (i18n 依赖) |
-| | ModelSelector | ❌ | ❌ | 模型选择器 (Redux 依赖) |
-| | LanguageSelect | ❌ | ❌ | 语言选择 |
-| | TranslateButton | ❌ | ❌ | 翻译按钮 (window.api 依赖) |
-| **composite** | | | | 复合组件 |
-| | - | - | - | 暂无复合组件 |
-| **未分类** | | | | 需要分类的组件 |
-| | Popups/* (16+ 文件) | ❌ | ❌ | 弹窗组件 (业务耦合) |
-| | RichEditor/* (30+ 文件) | ❌ | ❌ | 富文本编辑器 |
-| | MarkdownEditor/* | ❌ | ❌ | Markdown 编辑器 |
-| | MinApp/* | ❌ | ❌ | 迷你应用 (Redux 依赖) |
-| | Avatar/* | ❌ | ❌ | 头像组件 |
-| | ActionTools/* | ❌ | ❌ | 操作工具 |
-| | CodeBlockView/* | ❌ | ❌ | 代码块视图 (window.api 依赖) |
-| | ContextMenu | ❌ | ❌ | 右键菜单 (Electron API) |
-| | WindowControls | ❌ | ❌ | 窗口控制 (Electron API) |
-| | ErrorBoundary | ❌ | ❌ | 错误边界 (window.api 依赖) |
+| Category | Component Name | Migration Status | Refactoring Status | Description |
+| --------------- | ------------------------- | ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **base** | | | | 基础组件 |
+| | CopyButton | ✅ | ✅ | 复制按钮 |
+| | CustomTag | ✅ | ✅ | 自定义标签 |
+| | DividerWithText | ✅ | ✅ | 带文本的分隔线 |
+| | EmojiIcon | ✅ | ✅ | 表情图标 |
+| | ErrorBoundary | ✅ | ✅ | 错误边界 (通过 props 解耦) |
+| | StatusTag | ✅ | ✅ | 统一状态标签(合并了 ErrorTag、SuccessTag、WarnTag、InfoTag) |
+| | IndicatorLight | ✅ | ✅ | 指示灯 |
+| | Spinner | ✅ | ✅ | 加载动画 |
+| | TextBadge | ✅ | ✅ | 文本徽标 |
+| | CustomCollapse | ✅ | ✅ | 自定义折叠面板 |
+| **display** | | | | 显示组件 |
+| | Ellipsis | ✅ | ✅ | 文本省略 |
+| | ExpandableText | ✅ | ✅ | 可展开文本 |
+| | ThinkingEffect | ✅ | ✅ | 思考效果动画 |
+| | EmojiAvatar | ✅ | ✅ | 表情头像 |
+| | ListItem | ✅ | ✅ | 列表项 |
+| | MaxContextCount | ✅ | ✅ | 最大上下文数显示 |
+| | ProviderAvatar | ✅ | ✅ | 提供者头像 |
+| | CodeViewer | ❌ | ❌ | 代码查看器 (外部依赖) |
+| | OGCard | ❌ | ❌ | OG 卡片 |
+| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown 渲染器 |
+| | Preview/* | ❌ | ❌ | 预览组件 |
+| **layout** | | | | 布局组件 |
+| | HorizontalScrollContainer | ✅ | ❌ | 水平滚动容器 |
+| | Scrollbar | ✅ | ❌ | 滚动条 |
+| | Layout/* | ✅ | ✅ | 布局组件 |
+| | Tab/* | ❌ | ❌ | 标签页 (Redux 依赖) |
+| | TopView | ❌ | ❌ | 顶部视图 (window.api 依赖) |
+| **icons** | | | | 图标组件 |
+| | Icon | ✅ | ✅ | 图标工厂函数和预定义图标(合并了 CopyIcon、DeleteIcon、EditIcon、RefreshIcon、ResetIcon、ToolIcon、VisionIcon、WebSearchIcon、WrapIcon、UnWrapIcon、OcrIcon) |
+| | FileIcons | ✅ | ❌ | 文件图标 (FileSvgIcon、FilePngIcon) |
+| | ReasoningIcon | ✅ | ❌ | 推理图标 |
+| | SvgSpinners180Ring | ✅ | ❌ | 旋转加载图标 |
+| | ToolsCallingIcon | ✅ | ❌ | 工具调用图标 |
+| **interactive** | | | | 交互组件 |
+| | InfoTooltip | ✅ | ❌ | 信息提示 |
+| | HelpTooltip | ✅ | ❌ | 帮助提示 |
+| | WarnTooltip | ✅ | ❌ | 警告提示 |
+| | EditableNumber | ✅ | ❌ | 可编辑数字 |
+| | InfoPopover | ✅ | ❌ | 信息弹出框 |
+| | CollapsibleSearchBar | ✅ | ❌ | 可折叠搜索栏 |
+| | ImageToolButton | ✅ | ❌ | 图片工具按钮 |
+| | DraggableList | ✅ | ❌ | 可拖拽列表 |
+| | CodeEditor | ✅ | ❌ | 代码编辑器 |
+| | EmojiPicker | ❌ | ❌ | 表情选择器 (useTheme 依赖) |
+| | Selector | ✅ | ❌ | 选择器 (i18n 依赖) |
+| | ModelSelector | ❌ | ❌ | 模型选择器 (Redux 依赖) |
+| | LanguageSelect | ❌ | ❌ | 语言选择 |
+| | TranslateButton | ❌ | ❌ | 翻译按钮 (window.api 依赖) |
+| **composite** | | | | 复合组件 |
+| | - | - | - | 暂无复合组件 |
+| **未分类** | | | | 需要分类的组件 |
+| | Popups/* (16+ 文件) | ❌ | ❌ | 弹窗组件 (业务耦合) |
+| | RichEditor/* (30+ 文件) | ❌ | ❌ | 富文本编辑器 |
+| | MarkdownEditor/* | ❌ | ❌ | Markdown 编辑器 |
+| | MinApp/* | ❌ | ❌ | 迷你应用 (Redux 依赖) |
+| | Avatar/* | ❌ | ❌ | 头像组件 |
+| | ActionTools/* | ❌ | ❌ | 操作工具 |
+| | CodeBlockView/* | ❌ | ❌ | 代码块视图 (window.api 依赖) |
+| | ContextMenu | ❌ | ❌ | 右键菜单 (Electron API) |
+| | WindowControls | ❌ | ❌ | 窗口控制 (Electron API) |
+| | ErrorBoundary | ❌ | ❌ | 错误边界 (window.api 依赖) |
## 迁移步骤
diff --git a/packages/ui/MIGRATION_STATUS_EN.md b/packages/ui/MIGRATION_STATUS_EN.md
index 4fe4471173..8c62e36bc7 100644
--- a/packages/ui/MIGRATION_STATUS_EN.md
+++ b/packages/ui/MIGRATION_STATUS_EN.md
@@ -54,71 +54,71 @@ When submitting PRs, please place components in the correct directory based on t
## Component Status Table
-| Category | Component Name | Migration Status | Refactoring Status | Description |
-|----------|----------------|------------------|--------------------|-------------|
-| **base** | | | | Base components |
-| | CopyButton | ✅ | ✅ | Copy button |
-| | CustomTag | ✅ | ✅ | Custom tag |
-| | DividerWithText | ✅ | ✅ | Divider with text |
-| | EmojiIcon | ✅ | ✅ | Emoji icon |
-| | ErrorBoundary | ✅ | ✅ | Error boundary (decoupled via props) |
-| | StatusTag | ✅ | ✅ | Unified status tag (merged ErrorTag, SuccessTag, WarnTag, InfoTag) |
-| | IndicatorLight | ✅ | ✅ | Indicator light |
-| | Spinner | ✅ | ✅ | Loading spinner |
-| | TextBadge | ✅ | ✅ | Text badge |
-| | CustomCollapse | ✅ | ✅ | Custom collapse panel |
-| **display** | | | | Display components |
-| | Ellipsis | ✅ | ✅ | Text ellipsis |
-| | ExpandableText | ✅ | ✅ | Expandable text |
-| | ThinkingEffect | ✅ | ✅ | Thinking effect animation |
-| | EmojiAvatar | ✅ | ✅ | Emoji avatar |
-| | ListItem | ✅ | ✅ | List item |
-| | MaxContextCount | ✅ | ✅ | Max context count display |
-| | ProviderAvatar | ✅ | ✅ | Provider avatar |
-| | CodeViewer | ❌ | ❌ | Code viewer (external deps) |
-| | OGCard | ❌ | ❌ | OG card |
-| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown renderer |
-| | Preview/* | ❌ | ❌ | Preview components |
-| **layout** | | | | Layout components |
-| | HorizontalScrollContainer | ✅ | ❌ | Horizontal scroll container |
-| | Scrollbar | ✅ | ❌ | Scrollbar |
-| | Layout/* | ❌ | ❌ | Layout components |
-| | Tab/* | ❌ | ❌ | Tab (Redux dependency) |
-| | TopView | ❌ | ❌ | Top view (window.api dependency) |
-| **icons** | | | | Icon components |
-| | Icon | ✅ | ✅ | Icon factory function and predefined icons (merged CopyIcon, DeleteIcon, EditIcon, RefreshIcon, ResetIcon, ToolIcon, VisionIcon, WebSearchIcon, WrapIcon, UnWrapIcon, OcrIcon) |
-| | FileIcons | ✅ | ❌ | File icons (FileSvgIcon, FilePngIcon) |
-| | ReasoningIcon | ✅ | ❌ | Reasoning icon |
-| | SvgSpinners180Ring | ✅ | ❌ | Spinner loading icon |
-| | ToolsCallingIcon | ✅ | ❌ | Tools calling icon |
-| **interactive** | | | | Interactive components |
-| | InfoTooltip | ✅ | ❌ | Info tooltip |
-| | HelpTooltip | ✅ | ❌ | Help tooltip |
-| | WarnTooltip | ✅ | ❌ | Warning tooltip |
-| | EditableNumber | ✅ | ❌ | Editable number |
-| | InfoPopover | ✅ | ❌ | Info popover |
-| | CollapsibleSearchBar | ✅ | ❌ | Collapsible search bar |
-| | ImageToolButton | ✅ | ❌ | Image tool button |
-| | DraggableList | ✅ | ❌ | Draggable list |
-| | CodeEditor | ✅ | ❌ | Code editor |
-| | EmojiPicker | ❌ | ❌ | Emoji picker (useTheme dependency) |
-| | Selector | ✅ | ❌ | Selector (i18n dependency) |
-| | ModelSelector | ❌ | ❌ | Model selector (Redux dependency) |
-| | LanguageSelect | ❌ | ❌ | Language select |
-| | TranslateButton | ❌ | ❌ | Translate button (window.api dependency) |
-| **composite** | | | | Composite components |
-| | - | - | - | No composite components yet |
-| **Uncategorized** | | | | Components needing categorization |
-| | Popups/* (16+ files) | ❌ | ❌ | Popup components (business coupled) |
-| | RichEditor/* (30+ files) | ❌ | ❌ | Rich text editor |
-| | MarkdownEditor/* | ❌ | ❌ | Markdown editor |
-| | MinApp/* | ❌ | ❌ | Mini app (Redux dependency) |
-| | Avatar/* | ❌ | ❌ | Avatar components |
-| | ActionTools/* | ❌ | ❌ | Action tools |
-| | CodeBlockView/* | ❌ | ❌ | Code block view (window.api dependency) |
-| | ContextMenu | ❌ | ❌ | Context menu (Electron API) |
-| | WindowControls | ❌ | ❌ | Window controls (Electron API) |
-| | ErrorBoundary | ❌ | ❌ | Error boundary (window.api dependency) |
+| Category | Component Name | Migration Status | Refactoring Status | Description |
+| ----------------- | ------------------------- | ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| **base** | | | | Base components |
+| | CopyButton | ✅ | ✅ | Copy button |
+| | CustomTag | ✅ | ✅ | Custom tag |
+| | DividerWithText | ✅ | ✅ | Divider with text |
+| | EmojiIcon | ✅ | ✅ | Emoji icon |
+| | ErrorBoundary | ✅ | ✅ | Error boundary (decoupled via props) |
+| | StatusTag | ✅ | ✅ | Unified status tag (merged ErrorTag, SuccessTag, WarnTag, InfoTag) |
+| | IndicatorLight | ✅ | ✅ | Indicator light |
+| | Spinner | ✅ | ✅ | Loading spinner |
+| | TextBadge | ✅ | ✅ | Text badge |
+| | CustomCollapse | ✅ | ✅ | Custom collapse panel |
+| **display** | | | | Display components |
+| | Ellipsis | ✅ | ✅ | Text ellipsis |
+| | ExpandableText | ✅ | ✅ | Expandable text |
+| | ThinkingEffect | ✅ | ✅ | Thinking effect animation |
+| | EmojiAvatar | ✅ | ✅ | Emoji avatar |
+| | ListItem | ✅ | ✅ | List item |
+| | MaxContextCount | ✅ | ✅ | Max context count display |
+| | ProviderAvatar | ✅ | ✅ | Provider avatar |
+| | CodeViewer | ❌ | ❌ | Code viewer (external deps) |
+| | OGCard | ❌ | ❌ | OG card |
+| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown renderer |
+| | Preview/* | ❌ | ❌ | Preview components |
+| **layout** | | | | Layout components |
+| | HorizontalScrollContainer | ✅ | ❌ | Horizontal scroll container |
+| | Scrollbar | ✅ | ❌ | Scrollbar |
+| | Layout/* | ✅ | ✅ | Layout components |
+| | Tab/* | ❌ | ❌ | Tab (Redux dependency) |
+| | TopView | ❌ | ❌ | Top view (window.api dependency) |
+| **icons** | | | | Icon components |
+| | Icon | ✅ | ✅ | Icon factory function and predefined icons (merged CopyIcon, DeleteIcon, EditIcon, RefreshIcon, ResetIcon, ToolIcon, VisionIcon, WebSearchIcon, WrapIcon, UnWrapIcon, OcrIcon) |
+| | FileIcons | ✅ | ❌ | File icons (FileSvgIcon, FilePngIcon) |
+| | ReasoningIcon | ✅ | ❌ | Reasoning icon |
+| | SvgSpinners180Ring | ✅ | ❌ | Spinner loading icon |
+| | ToolsCallingIcon | ✅ | ❌ | Tools calling icon |
+| **interactive** | | | | Interactive components |
+| | InfoTooltip | ✅ | ❌ | Info tooltip |
+| | HelpTooltip | ✅ | ❌ | Help tooltip |
+| | WarnTooltip | ✅ | ❌ | Warning tooltip |
+| | EditableNumber | ✅ | ❌ | Editable number |
+| | InfoPopover | ✅ | ❌ | Info popover |
+| | CollapsibleSearchBar | ✅ | ❌ | Collapsible search bar |
+| | ImageToolButton | ✅ | ❌ | Image tool button |
+| | DraggableList | ✅ | ❌ | Draggable list |
+| | CodeEditor | ✅ | ❌ | Code editor |
+| | EmojiPicker | ❌ | ❌ | Emoji picker (useTheme dependency) |
+| | Selector | ✅ | ❌ | Selector (i18n dependency) |
+| | ModelSelector | ❌ | ❌ | Model selector (Redux dependency) |
+| | LanguageSelect | ❌ | ❌ | Language select |
+| | TranslateButton | ❌ | ❌ | Translate button (window.api dependency) |
+| **composite** | | | | Composite components |
+| | - | - | - | No composite components yet |
+| **Uncategorized** | | | | Components needing categorization |
+| | Popups/* (16+ files) | ❌ | ❌ | Popup components (business coupled) |
+| | RichEditor/* (30+ files) | ❌ | ❌ | Rich text editor |
+| | MarkdownEditor/* | ❌ | ❌ | Markdown editor |
+| | MinApp/* | ❌ | ❌ | Mini app (Redux dependency) |
+| | Avatar/* | ❌ | ❌ | Avatar components |
+| | ActionTools/* | ❌ | ❌ | Action tools |
+| | CodeBlockView/* | ❌ | ❌ | Code block view (window.api dependency) |
+| | ContextMenu | ❌ | ❌ | Context menu (Electron API) |
+| | WindowControls | ❌ | ❌ | Window controls (Electron API) |
+| | ErrorBoundary | ❌ | ❌ | Error boundary (window.api dependency) |
## Migration Steps
diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts
index ab1d3d40bd..c5f3727e34 100644
--- a/packages/ui/src/components/index.ts
+++ b/packages/ui/src/components/index.ts
@@ -22,6 +22,7 @@ export { ProviderAvatar } from './display/ProviderAvatar'
export { default as ThinkingEffect } from './display/ThinkingEffect'
// Layout Components
+export { Box, Center, ColFlex, Flex, RowFlex, SpaceBetweenRowFlex } from './layout/Flex'
export { default as HorizontalScrollContainer } from './layout/HorizontalScrollContainer'
export { default as Scrollbar } from './layout/Scrollbar'
diff --git a/packages/ui/src/components/layout/Flex/index.tsx b/packages/ui/src/components/layout/Flex/index.tsx
new file mode 100644
index 0000000000..522a5574d7
--- /dev/null
+++ b/packages/ui/src/components/layout/Flex/index.tsx
@@ -0,0 +1,63 @@
+import React from 'react'
+
+import { cn } from '../../../utils'
+
+export interface BoxProps extends React.ComponentProps<'div'> {}
+
+export const Box = ({ children, className, ...props }: BoxProps & { children?: React.ReactNode }) => {
+ return (
+
+ {children}
+
+ )
+}
+
+export interface FlexProps extends BoxProps {}
+
+export const Flex = ({ children, className, ...props }: FlexProps & { children?: React.ReactNode }) => {
+ return (
+
+ {children}
+
+ )
+}
+
+export const RowFlex = ({ children, className, ...props }: FlexProps & { children?: React.ReactNode }) => {
+ return (
+
+ {children}
+
+ )
+}
+
+export const SpaceBetweenRowFlex = ({ children, className, ...props }: FlexProps & { children?: React.ReactNode }) => {
+ return (
+
+ {children}
+
+ )
+}
+export const ColFlex = ({ children, className, ...props }: FlexProps & { children?: React.ReactNode }) => {
+ return (
+
+ {children}
+
+ )
+}
+
+export const Center = ({ children, className, ...props }: FlexProps & { children?: React.ReactNode }) => {
+ return (
+
+ {children}
+
+ )
+}
+
+export default {
+ Box,
+ Flex,
+ RowFlex,
+ SpaceBetweenRowFlex,
+ ColFlex,
+ Center
+}
diff --git a/src/renderer/src/assets/styles/index.css b/src/renderer/src/assets/styles/index.css
index b344e60ae6..d48eb615d1 100644
--- a/src/renderer/src/assets/styles/index.css
+++ b/src/renderer/src/assets/styles/index.css
@@ -11,18 +11,19 @@
@import '../fonts/ubuntu/ubuntu.css';
@import '../fonts/country-flag-fonts/flag.css';
-*,
-*::before,
-*::after {
- box-sizing: border-box;
- /* margin: 0; */
- font-weight: normal;
+@layer base {
+ *,
+ *::before,
+ *::after {
+ box-sizing: border-box;
+ /* margin: 0; */
+ font-weight: normal;
+ }
}
*:focus {
outline: none;
}
-
* {
-webkit-tap-highlight-color: transparent;
}
diff --git a/src/renderer/src/components/CodeBlockView/StatusBar.tsx b/src/renderer/src/components/CodeBlockView/StatusBar.tsx
index 72589b9045..6824ba5e42 100644
--- a/src/renderer/src/components/CodeBlockView/StatusBar.tsx
+++ b/src/renderer/src/components/CodeBlockView/StatusBar.tsx
@@ -1,4 +1,4 @@
-import { Flex } from 'antd'
+import { Flex } from '@cherrystudio/ui'
import type { FC, ReactNode } from 'react'
import { memo } from 'react'
import styled from 'styled-components'
diff --git a/src/renderer/src/components/CodeToolbar/__tests__/CodeToolbar.test.tsx b/src/renderer/src/components/CodeToolbar/__tests__/CodeToolbar.test.tsx
index 4f75409c8a..681f9fd753 100644
--- a/src/renderer/src/components/CodeToolbar/__tests__/CodeToolbar.test.tsx
+++ b/src/renderer/src/components/CodeToolbar/__tests__/CodeToolbar.test.tsx
@@ -19,7 +19,7 @@ const mocks = vi.hoisted(() => ({
{children}
)),
- HStack: vi.fn(({ children, className }) => (
+ RowFlex: vi.fn(({ children, className }) => (
{children}
@@ -43,8 +43,8 @@ vi.mock('antd', () => ({
Tooltip: mocks.Tooltip
}))
-vi.mock('@renderer/components/Layout', () => ({
- HStack: mocks.HStack
+vi.mock('@cherrystudio/ui', () => ({
+ RowFlex: mocks.RowFlex
}))
vi.mock('./styles', () => ({
diff --git a/src/renderer/src/components/CodeToolbar/toolbar.tsx b/src/renderer/src/components/CodeToolbar/toolbar.tsx
index 7b78d8e536..a87cce5b19 100644
--- a/src/renderer/src/components/CodeToolbar/toolbar.tsx
+++ b/src/renderer/src/components/CodeToolbar/toolbar.tsx
@@ -1,5 +1,5 @@
+import { RowFlex } from '@cherrystudio/ui'
import type { ActionTool } from '@renderer/components/ActionTools'
-import { HStack } from '@renderer/components/Layout'
import { Tooltip } from 'antd'
import { EllipsisVertical } from 'lucide-react'
import { memo, useMemo, useState } from 'react'
@@ -61,7 +61,7 @@ const StickyWrapper = styled.div`
z-index: 10;
`
-const ToolbarWrapper = styled(HStack)`
+const ToolbarWrapper = styled(RowFlex)`
position: absolute;
align-items: center;
bottom: 0.3rem;
diff --git a/src/renderer/src/components/HealthStatusIndicator/indicator.tsx b/src/renderer/src/components/HealthStatusIndicator/indicator.tsx
index 2807549e28..9b65d58f29 100644
--- a/src/renderer/src/components/HealthStatusIndicator/indicator.tsx
+++ b/src/renderer/src/components/HealthStatusIndicator/indicator.tsx
@@ -1,5 +1,6 @@
import { CheckCircleFilled, CloseCircleFilled, ExclamationCircleFilled, LoadingOutlined } from '@ant-design/icons'
-import { Flex, Tooltip, Typography } from 'antd'
+import { Flex } from '@cherrystudio/ui'
+import { Tooltip, Typography } from 'antd'
import React, { memo } from 'react'
import styled from 'styled-components'
@@ -48,7 +49,7 @@ const HealthStatusIndicator: React.FC = ({
}
return (
-
+
{latencyText && {latencyText}}
{icon}
diff --git a/src/renderer/src/components/HealthStatusIndicator/useHealthStatus.tsx b/src/renderer/src/components/HealthStatusIndicator/useHealthStatus.tsx
index 1027324eeb..4ecedb18c9 100644
--- a/src/renderer/src/components/HealthStatusIndicator/useHealthStatus.tsx
+++ b/src/renderer/src/components/HealthStatusIndicator/useHealthStatus.tsx
@@ -1,5 +1,5 @@
+import { Flex } from '@cherrystudio/ui'
import { HealthStatus } from '@renderer/types/healthCheck'
-import { Flex } from 'antd'
import React from 'react'
import { useTranslation } from 'react-i18next'
@@ -77,7 +77,7 @@ export const useHealthStatus = ({ results, showLatency = false }: UseHealthStatu
return (
-
+
{statusText}
{result.label}
diff --git a/src/renderer/src/components/Layout/index.ts b/src/renderer/src/components/Layout/index.ts
deleted file mode 100644
index 6ebc5788c5..0000000000
--- a/src/renderer/src/components/Layout/index.ts
+++ /dev/null
@@ -1,159 +0,0 @@
-import styled from 'styled-components'
-
-interface ContainerProps {
- padding?: string
-}
-
-type PxValue = number | string
-
-export interface BoxProps {
- width?: PxValue
- height?: PxValue
- w?: PxValue
- h?: PxValue
- color?: string
- background?: string
- flex?: string | number
- position?: string
- left?: PxValue
- top?: PxValue
- right?: PxValue
- bottom?: PxValue
- opacity?: string | number
- borderRadius?: PxValue
- border?: string
- gap?: PxValue
- mt?: PxValue
- marginTop?: PxValue
- mb?: PxValue
- marginBottom?: PxValue
- ml?: PxValue
- marginLeft?: PxValue
- mr?: PxValue
- marginRight?: PxValue
- m?: string
- margin?: string
- pt?: PxValue
- paddingTop?: PxValue
- pb?: PxValue
- paddingBottom?: PxValue
- pl?: PxValue
- paddingLeft?: PxValue
- pr?: PxValue
- paddingRight?: PxValue
- p?: string
- padding?: string
-}
-
-export interface StackProps extends BoxProps {
- justifyContent?: 'center' | 'flex-start' | 'flex-end' | 'space-between'
- alignItems?: 'center' | 'flex-start' | 'flex-end' | 'space-between'
- flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse'
-}
-
-export interface ButtonProps extends StackProps {
- color?: string
- isDisabled?: boolean
- isLoading?: boolean
- background?: string
- border?: string
- fontSize?: string
-}
-
-const cssRegex = /(px|vw|vh|%|auto)$/g
-
-const getElementValue = (value?: PxValue) => {
- if (!value) {
- return value
- }
-
- if (typeof value === 'number') {
- return value + 'px'
- }
-
- if (value.match(cssRegex)) {
- return value
- }
-
- return value + 'px'
-}
-
-export const Box = styled.div`
- width: ${(props) => (props.width || props.w ? getElementValue(props.width ?? props.w) : 'auto')};
- height: ${(props) => (props.height || props.h ? getElementValue(props.height || props.h) : 'auto')};
- color: ${(props) => props.color || 'default'};
- background: ${(props) => props.background || 'default'};
- flex: ${(props) => props.flex || 'none'};
- position: ${(props) => props.position || 'default'};
- left: ${(props) => getElementValue(props.left) || 'auto'};
- right: ${(props) => getElementValue(props.right) || 'auto'};
- bottom: ${(props) => getElementValue(props.bottom) || 'auto'};
- top: ${(props) => getElementValue(props.top) || 'auto'};
- gap: ${(p) => (p.gap ? getElementValue(p.gap) : 0)};
- opacity: ${(props) => props.opacity ?? 1};
- border-radius: ${(props) => getElementValue(props.borderRadius) || 0};
- box-sizing: border-box;
- border: ${(props) => props?.border || 'none'};
- gap: ${(p) => (p.gap ? getElementValue(p.gap) : 0)};
- margin: ${(props) => (props.m || props.margin ? (props.m ?? props.margin) : 'none')};
- margin-top: ${(props) => (props.mt || props.marginTop ? getElementValue(props.mt || props.marginTop) : 'default')};
- margin-bottom: ${(props) =>
- props.mb || props.marginBottom ? getElementValue(props.mb ?? props.marginBottom) : 'default'};
- margin-left: ${(props) => (props.ml || props.marginLeft ? getElementValue(props.ml ?? props.marginLeft) : 'default')};
- margin-right: ${(props) =>
- props.mr || props.marginRight ? getElementValue(props.mr ?? props.marginRight) : 'default'};
- padding: ${(props) => (props.p || props.padding ? (props.p ?? props.padding) : 'none')};
- padding-top: ${(props) => (props.pt || props.paddingTop ? getElementValue(props.pt ?? props.paddingTop) : 'auto')};
- padding-bottom: ${(props) =>
- props.pb || props.paddingBottom ? getElementValue(props.pb ?? props.paddingBottom) : 'auto'};
- padding-left: ${(props) => (props.pl || props.paddingLeft ? getElementValue(props.pl ?? props.paddingLeft) : 'auto')};
- padding-right: ${(props) =>
- props.pr || props.paddingRight ? getElementValue(props.pr ?? props.paddingRight) : 'auto'};
-`
-
-export const Stack = styled(Box)`
- display: flex;
- justify-content: ${(props) => props.justifyContent ?? 'flex-start'};
- align-items: ${(props) => props.alignItems ?? 'flex-start'};
- flex-direction: ${(props) => props.flexDirection ?? 'row'};
-`
-
-export const Center = styled(Stack)`
- justify-content: center;
- align-items: center;
-`
-
-export const HStack = styled(Stack)`
- flex-direction: row;
-`
-
-export const HSpaceBetweenStack = styled(HStack)`
- justify-content: space-between;
-`
-
-export const VStack = styled(Stack)`
- flex-direction: column;
-`
-
-export const BaseTypography = styled(Box)<{
- fontSize?: number
- lineHeight?: string
- fontWeigth?: number | string
- color?: string
- textAlign?: string
-}>`
- font-size: ${(props) => (props.fontSize ? getElementValue(props.fontSize) : '16px')};
- line-height: ${(props) => (props.lineHeight ? getElementValue(props.lineHeight) : 'normal')};
- font-weight: ${(props) => props.fontWeigth || 'normal'};
- color: ${(props) => props.color || '#fff'};
- text-align: ${(props) => props.textAlign || 'left'};
-`
-
-export const Container = styled.main`
- display: flex;
- flex-direction: column;
- width: 100%;
- box-sizing: border-box;
- flex: 1;
- padding: ${(p) => p.padding ?? '0 18px'};
-`
diff --git a/src/renderer/src/components/NutstorePathSelector.tsx b/src/renderer/src/components/NutstorePathSelector.tsx
index 6393bad4c1..531dc086bf 100644
--- a/src/renderer/src/components/NutstorePathSelector.tsx
+++ b/src/renderer/src/components/NutstorePathSelector.tsx
@@ -1,3 +1,4 @@
+import { RowFlex } from '@cherrystudio/ui'
import { loggerService } from '@logger'
import { FolderIcon as NutstoreFolderIcon } from '@renderer/components/Icons/NutstoreIcons'
import { Button, Input } from 'antd'
@@ -5,8 +6,6 @@ import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
-import { HStack } from './Layout'
-
interface NewFolderProps {
onConfirm: (name: string) => void
onCancel: () => void
@@ -215,7 +214,7 @@ export function NutstorePathSelector(props: Props) {
)
}
-const FooterContainer = styled(HStack)`
+const FooterContainer = styled(RowFlex)`
background: transparent;
margin-top: 12px;
padding: 0;
@@ -233,21 +232,21 @@ interface FooterProps {
export function NustorePathSelectorFooter(props: FooterProps) {
const { t } = useTranslation()
return (
-
-
+
+
-
-
+
+
-
+
)
}
diff --git a/src/renderer/src/components/Popups/AddAssistantPopup.tsx b/src/renderer/src/components/Popups/AddAssistantPopup.tsx
index e891b87818..0d39ff4197 100644
--- a/src/renderer/src/components/Popups/AddAssistantPopup.tsx
+++ b/src/renderer/src/components/Popups/AddAssistantPopup.tsx
@@ -1,3 +1,4 @@
+import { RowFlex } from '@cherrystudio/ui'
import { TopView } from '@renderer/components/TopView'
import { useAgents } from '@renderer/hooks/useAgents'
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
@@ -16,7 +17,6 @@ import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import EmojiIcon from '../EmojiIcon'
-import { HStack } from '../Layout'
import Scrollbar from '../Scrollbar'
interface Props {
@@ -174,7 +174,7 @@ const PopupContainer: React.FC = ({ resolve }) => {
}}
closeIcon={null}
footer={null}>
-
+
@@ -191,7 +191,7 @@ const PopupContainer: React.FC = ({ resolve }) => {
variant="borderless"
size="middle"
/>
-
+
{take(agents, 100).map((agent, index) => (
@@ -200,10 +200,10 @@ const PopupContainer: React.FC = ({ resolve }) => {
onClick={() => onCreateAssistant(agent)}
className={`agent-item ${agent.id === 'default' ? 'default' : ''} ${index === selectedIndex ? 'keyboard-selected' : ''}`}
onMouseEnter={() => setSelectedIndex(index)}>
-
+
{agent.name}
-
+
{agent.id === 'default' && {t('agents.tag.system')}}
{agent.type === 'agent' && {t('agents.tag.agent')}}
{agent.id === 'new' && {t('agents.tag.new')}}
diff --git a/src/renderer/src/components/Popups/ApiKeyListPopup/item.tsx b/src/renderer/src/components/Popups/ApiKeyListPopup/item.tsx
index 3ddcc383cd..e85d8093e5 100644
--- a/src/renderer/src/components/Popups/ApiKeyListPopup/item.tsx
+++ b/src/renderer/src/components/Popups/ApiKeyListPopup/item.tsx
@@ -1,10 +1,11 @@
+import { Flex } from '@cherrystudio/ui'
import { type HealthResult, HealthStatusIndicator } from '@renderer/components/HealthStatusIndicator'
import { EditIcon } from '@renderer/components/Icons'
import { StreamlineGoodHealthAndWellBeing } from '@renderer/components/Icons/SVGIcon'
import type { ApiKeyWithStatus } from '@renderer/types/healthCheck'
import { maskApiKey } from '@renderer/utils/api'
import type { InputRef } from 'antd'
-import { Button, Flex, Input, List, Popconfirm, Tooltip, Typography } from 'antd'
+import { Button, Input, List, Popconfirm, Tooltip, Typography } from 'antd'
import { Check, Minus, X } from 'lucide-react'
import type { FC } from 'react'
import { memo, useEffect, useRef, useState } from 'react'
@@ -104,7 +105,7 @@ const ApiKeyItem: FC = ({
spellCheck={false}
disabled={disabled}
/>
-
+
-
+
-
+
{showHealthCheck && (