diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1957bd3e25..4696fd16e3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,5 @@ /src/renderer/src/store/ @0xfullex +/src/renderer/src/databases/ @0xfullex /src/main/services/ConfigManager.ts @0xfullex /packages/shared/IpcChannel.ts @0xfullex /src/main/ipc.ts @0xfullex @@ -9,3 +10,4 @@ /src/renderer/src/data/ @0xfullex /packages/ui/ @MyPrototypeWhat + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 72e175baf5..03b71ecec7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,6 +3,18 @@ 1. Consider creating this PR as draft: https://github.com/CherryHQ/cherry-studio/blob/main/CONTRIBUTING.md --> + + ### What this PR does Before this PR: diff --git a/.github/workflows/auto-i18n.yml b/.github/workflows/auto-i18n.yml index e45a65ce08..a6c1e3791a 100644 --- a/.github/workflows/auto-i18n.yml +++ b/.github/workflows/auto-i18n.yml @@ -1,9 +1,10 @@ name: Auto I18N env: - API_KEY: ${{ secrets.TRANSLATE_API_KEY }} - MODEL: ${{ vars.AUTO_I18N_MODEL || 'deepseek/deepseek-v3.1'}} - BASE_URL: ${{ vars.AUTO_I18N_BASE_URL || 'https://api.ppinfra.com/openai'}} + TRANSLATION_API_KEY: ${{ secrets.TRANSLATE_API_KEY }} + TRANSLATION_MODEL: ${{ vars.AUTO_I18N_MODEL || 'deepseek/deepseek-v3.1'}} + TRANSLATION_BASE_URL: ${{ vars.AUTO_I18N_BASE_URL || 'https://api.ppinfra.com/openai'}} + TRANSLATION_BASE_LOCALE: ${{ vars.AUTO_I18N_BASE_LOCALE || 'en-us'}} on: pull_request: @@ -29,6 +30,7 @@ jobs: uses: actions/setup-node@v5 with: node-version: 20 + package-manager-cache: false - name: 📦 Install dependencies in isolated directory run: | @@ -42,7 +44,7 @@ jobs: echo "NODE_PATH=/tmp/translation-deps/node_modules" >> $GITHUB_ENV - name: 🏃‍♀️ Translate - run: npx tsx scripts/auto-translate-i18n.ts + run: npx tsx scripts/sync-i18n.ts && npx tsx scripts/auto-translate-i18n.ts - name: 🔍 Format run: cd /tmp/translation-deps && npx biome format --config-path /home/runner/work/cherry-studio/cherry-studio/biome.jsonc --write /home/runner/work/cherry-studio/cherry-studio/src/renderer/src/i18n/ diff --git a/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch b/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch new file mode 100644 index 0000000000..7aeb4ea9cf --- /dev/null +++ b/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch @@ -0,0 +1,131 @@ +diff --git a/dist/index.mjs b/dist/index.mjs +index b3f018730a93639aad7c203f15fb1aeb766c73f4..ade2a43d66e9184799d072153df61ef7be4ea110 100644 +--- a/dist/index.mjs ++++ b/dist/index.mjs +@@ -296,7 +296,14 @@ var HuggingFaceResponsesLanguageModel = class { + metadata: huggingfaceOptions == null ? void 0 : huggingfaceOptions.metadata, + instructions: huggingfaceOptions == null ? void 0 : huggingfaceOptions.instructions, + ...preparedTools && { tools: preparedTools }, +- ...preparedToolChoice && { tool_choice: preparedToolChoice } ++ ...preparedToolChoice && { tool_choice: preparedToolChoice }, ++ ...(huggingfaceOptions?.reasoningEffort != null && { ++ reasoning: { ++ ...(huggingfaceOptions?.reasoningEffort != null && { ++ effort: huggingfaceOptions.reasoningEffort, ++ }), ++ }, ++ }), + }; + return { args: baseArgs, warnings }; + } +@@ -365,6 +372,20 @@ var HuggingFaceResponsesLanguageModel = class { + } + break; + } ++ case 'reasoning': { ++ for (const contentPart of part.content) { ++ content.push({ ++ type: 'reasoning', ++ text: contentPart.text, ++ providerMetadata: { ++ huggingface: { ++ itemId: part.id, ++ }, ++ }, ++ }); ++ } ++ break; ++ } + case "mcp_call": { + content.push({ + type: "tool-call", +@@ -519,6 +540,11 @@ var HuggingFaceResponsesLanguageModel = class { + id: value.item.call_id, + toolName: value.item.name + }); ++ } else if (value.item.type === 'reasoning') { ++ controller.enqueue({ ++ type: 'reasoning-start', ++ id: value.item.id, ++ }); + } + return; + } +@@ -570,6 +596,22 @@ var HuggingFaceResponsesLanguageModel = class { + }); + return; + } ++ if (isReasoningDeltaChunk(value)) { ++ controller.enqueue({ ++ type: 'reasoning-delta', ++ id: value.item_id, ++ delta: value.delta, ++ }); ++ return; ++ } ++ ++ if (isReasoningEndChunk(value)) { ++ controller.enqueue({ ++ type: 'reasoning-end', ++ id: value.item_id, ++ }); ++ return; ++ } + }, + flush(controller) { + controller.enqueue({ +@@ -593,7 +635,8 @@ var HuggingFaceResponsesLanguageModel = class { + var huggingfaceResponsesProviderOptionsSchema = z2.object({ + metadata: z2.record(z2.string(), z2.string()).optional(), + instructions: z2.string().optional(), +- strictJsonSchema: z2.boolean().optional() ++ strictJsonSchema: z2.boolean().optional(), ++ reasoningEffort: z2.string().optional(), + }); + var huggingfaceResponsesResponseSchema = z2.object({ + id: z2.string(), +@@ -727,12 +770,31 @@ var responseCreatedChunkSchema = z2.object({ + model: z2.string() + }) + }); ++var reasoningTextDeltaChunkSchema = z2.object({ ++ type: z2.literal('response.reasoning_text.delta'), ++ item_id: z2.string(), ++ output_index: z2.number(), ++ content_index: z2.number(), ++ delta: z2.string(), ++ sequence_number: z2.number(), ++}); ++ ++var reasoningTextEndChunkSchema = z2.object({ ++ type: z2.literal('response.reasoning_text.done'), ++ item_id: z2.string(), ++ output_index: z2.number(), ++ content_index: z2.number(), ++ text: z2.string(), ++ sequence_number: z2.number(), ++}); + var huggingfaceResponsesChunkSchema = z2.union([ + responseOutputItemAddedSchema, + responseOutputItemDoneSchema, + textDeltaChunkSchema, + responseCompletedChunkSchema, + responseCreatedChunkSchema, ++ reasoningTextDeltaChunkSchema, ++ reasoningTextEndChunkSchema, + z2.object({ type: z2.string() }).loose() + // fallback for unknown chunks + ]); +@@ -751,6 +813,12 @@ function isResponseCompletedChunk(chunk) { + function isResponseCreatedChunk(chunk) { + return chunk.type === "response.created"; + } ++function isReasoningDeltaChunk(chunk) { ++ return chunk.type === 'response.reasoning_text.delta'; ++} ++function isReasoningEndChunk(chunk) { ++ return chunk.type === 'response.reasoning_text.done'; ++} + + // src/huggingface-provider.ts + function createHuggingFace(options = {}) { diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 408057252b..88f034976f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,28 @@ The Test Plan aims to provide users with a more stable application experience an ### Other Suggestions - **Contact Developers**: Before submitting a PR, you can contact the developers first to discuss or get help. -- **Become a Core Developer**: If you contribute to the project consistently, congratulations, you can become a core developer and gain project membership status. Please check our [Membership Guide](https://github.com/CherryHQ/community/blob/main/docs/membership.en.md). + +## Important Contribution Guidelines & Focus Areas + +Please review the following critical information before submitting your Pull Request: + +### Temporary Restriction on Data-Changing Feature PRs 🚫 + +**Currently, we are NOT accepting feature Pull Requests that introduce changes to our Redux data models or IndexedDB schemas.** + +Our core team is currently focused on significant architectural updates that involve these data structures. To ensure stability and focus during this period, contributions of this nature will be temporarily managed internally. + +* **PRs that require changes to Redux state shape or IndexedDB schemas will be closed.** +* **This restriction is temporary and will be lifted with the release of `v2.0.0`.** You can track the progress of `v2.0.0` and its related discussions on issue [#10162](https://github.com/YOUR_ORG/YOUR_REPO/issues/10162) (please replace with your actual repo link). + +We highly encourage contributions for: +* Bug fixes 🐞 +* Performance improvements 🚀 +* Documentation updates 📚 +* Features that **do not** alter Redux data models or IndexedDB schemas (e.g., UI enhancements, new components, minor refactors). ✨ + +We appreciate your understanding and continued support during this important development phase. Thank you! + ## Contact Us diff --git a/README.md b/README.md index 634a4fc73d..c3d3f915a1 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@

English | 中文 | Official Site | Documents | Development | Feedback

- + [![][deepwiki-shield]][deepwiki-link] [![][twitter-shield]][twitter-link] [![][discord-shield]][discord-link] @@ -45,7 +45,7 @@
- + [![][github-release-shield]][github-release-link] [![][github-nightly-shield]][github-nightly-link] [![][github-contributors-shield]][github-contributors-link] @@ -248,10 +248,10 @@ The Enterprise Edition addresses core challenges in team collaboration by centra | Feature | Community Edition | Enterprise Edition | | :---------------- | :----------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | -| **Open Source** | ✅ Yes | ⭕️ Partially released to customers | +| **Open Source** | ✅ Yes | ⭕️ Partially released to customers | | **Cost** | Free for Personal Use / Commercial License | Buyout / Subscription Fee | | **Admin Backend** | — | ● Centralized **Model** Access
● **Employee** Management
● Shared **Knowledge Base**
● **Access** Control
● **Data** Backup | -| **Server** | — | ✅ Dedicated Private Deployment | +| **Server** | — | ✅ Dedicated Private Deployment | ## Get the Enterprise Edition diff --git a/docs/CONTRIBUTING.zh.md b/docs/CONTRIBUTING.zh.md index 7574990cd4..67193ed098 100644 --- a/docs/CONTRIBUTING.zh.md +++ b/docs/CONTRIBUTING.zh.md @@ -69,7 +69,28 @@ git commit --signoff -m "Your commit message" ### 其他建议 - **联系开发者**:在提交 PR 之前,您可以先和开发者进行联系,共同探讨或者获取帮助。 -- **成为核心开发者**:如果您能够稳定为项目贡献,恭喜您可以成为项目核心开发者,获取到项目成员身份。请查看我们的[成员指南](https://github.com/CherryHQ/community/blob/main/membership.md) + +## 重要贡献指南与关注点 + +在提交 Pull Request 之前,请务必阅读以下关键信息: + +### 🚫 暂时限制涉及数据更改的功能性 PR + +**目前,我们不接受涉及 Redux 数据模型或 IndexedDB schema 变更的功能性 Pull Request。** + +我们的核心团队目前正专注于涉及这些数据结构的关键架构更新和基础工作。为确保在此期间的稳定性与专注,此类贡献将暂时由内部进行管理。 + +* **需要更改 Redux 状态结构或 IndexedDB schema 的 PR 将会被关闭。** +* **此限制是临时性的,并将在 `v2.0.0` 版本发布后解除。** 您可以通过 Issue [#10162](https://github.com/YOUR_ORG/YOUR_REPO/issues/10162) (请替换为您的实际仓库链接) 跟踪 `v2.0.0` 的进展及相关讨论。 + +我们非常鼓励以下类型的贡献: +* 错误修复 🐞 +* 性能改进 🚀 +* 文档更新 📚 +* 不改变 Redux 数据模型或 IndexedDB schema 的功能(例如,UI 增强、新组件、小型重构)。✨ + +感谢您在此重要开发阶段的理解与持续支持。谢谢! + ## 联系我们 diff --git a/package.json b/package.json index e9f843199c..df517feaee 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "@agentic/tavily": "^7.3.3", "@ai-sdk/amazon-bedrock": "^3.0.35", "@ai-sdk/google-vertex": "^3.0.40", + "@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.4#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch", "@ai-sdk/mistral": "^2.0.19", "@ai-sdk/perplexity": "^2.0.13", "@ant-design/v5-patch-for-react-19": "^1.0.3", @@ -130,8 +131,8 @@ "@cherrystudio/embedjs-ollama": "^0.1.31", "@cherrystudio/embedjs-openai": "^0.1.31", "@cherrystudio/extension-table-plus": "workspace:^", - "@cherrystudio/ui": "workspace:*", "@cherrystudio/openai": "^6.5.0", + "@cherrystudio/ui": "workspace:*", "@dnd-kit/core": "^6.3.1", "@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/sortable": "^10.0.0", @@ -152,7 +153,7 @@ "@modelcontextprotocol/sdk": "^1.17.5", "@mozilla/readability": "^0.6.0", "@notionhq/client": "^2.2.15", - "@openrouter/ai-sdk-provider": "^1.1.2", + "@openrouter/ai-sdk-provider": "^1.2.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-trace-otlp-http": "^0.200.0", @@ -395,7 +396,8 @@ "@img/sharp-linux-arm": "0.34.4", "@img/sharp-linux-arm64": "0.34.4", "@img/sharp-linux-x64": "0.34.4", - "@img/sharp-win32-x64": "0.34.4" + "@img/sharp-win32-x64": "0.34.4", + "openai@npm:5.12.2": "npm:@cherrystudio/openai@6.5.0" }, "packageManager": "yarn@4.9.1", "lint-staged": { diff --git a/packages/aiCore/src/core/providers/schemas.ts b/packages/aiCore/src/core/providers/schemas.ts index 147baf2c97..7ca4f6b0c8 100644 --- a/packages/aiCore/src/core/providers/schemas.ts +++ b/packages/aiCore/src/core/providers/schemas.ts @@ -7,6 +7,7 @@ import { createAzure } from '@ai-sdk/azure' import { type AzureOpenAIProviderSettings } from '@ai-sdk/azure' import { createDeepSeek } from '@ai-sdk/deepseek' import { createGoogleGenerativeAI } from '@ai-sdk/google' +import { createHuggingFace } from '@ai-sdk/huggingface' import { createOpenAI, type OpenAIProviderSettings } from '@ai-sdk/openai' import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import type { LanguageModelV2 } from '@ai-sdk/provider' @@ -29,7 +30,8 @@ export const baseProviderIds = [ 'azure', 'azure-responses', 'deepseek', - 'openrouter' + 'openrouter', + 'huggingface' ] as const /** @@ -133,6 +135,12 @@ export const baseProviders = [ name: 'OpenRouter', creator: createOpenRouter, supportsImageGeneration: true + }, + { + id: 'huggingface', + name: 'HuggingFace', + creator: createHuggingFace, + supportsImageGeneration: true } ] as const satisfies BaseProvider[] diff --git a/packages/shared/IpcChannel.ts b/packages/shared/IpcChannel.ts index 334e00424f..51c9043fe5 100644 --- a/packages/shared/IpcChannel.ts +++ b/packages/shared/IpcChannel.ts @@ -138,6 +138,7 @@ export enum IpcChannel { Windows_Close = 'window:close', Windows_IsMaximized = 'window:is-maximized', Windows_MaximizedChanged = 'window:maximized-changed', + Windows_NavigateToAbout = 'window:navigate-to-about', KnowledgeBase_Create = 'knowledge-base:create', KnowledgeBase_Reset = 'knowledge-base:reset', diff --git a/packages/ui/MIGRATION_STATUS.md b/packages/ui/MIGRATION_STATUS.md index 4206242b91..8c62e36bc7 100644 --- a/packages/ui/MIGRATION_STATUS.md +++ b/packages/ui/MIGRATION_STATUS.md @@ -1,152 +1,151 @@ -# UI 组件库迁移状态 +# UI Component Library Migration Status -## 使用示例 +## Usage Example ```typescript -// 从 @cherrystudio/ui 导入组件 -import { Spinner, DividerWithText, InfoTooltip, CustomTag } from '@cherrystudio/ui' +// Import components from @cherrystudio/ui +import { Spinner, DividerWithText, InfoTooltip } from '@cherrystudio/ui' -// 在组件中使用 +// Use in components function MyComponent() { return (
- - - 标签 + +
) } ``` -## 目录结构说明 +## Directory Structure ```text @packages/ui/ ├── src/ -│ ├── components/ # 组件主目录 -│ │ ├── base/ # 基础组件(按钮、输入框、标签等) -│ │ ├── display/ # 显示组件(卡片、列表、表格等) -│ │ ├── layout/ # 布局组件(容器、网格、间距等) -│ │ ├── icons/ # 图标组件 -│ │ ├── interactive/ # 交互组件(弹窗、提示、下拉等) -│ │ └── composite/ # 复合组件(多个基础组件组合而成) -│ ├── hooks/ # 自定义 React Hooks -│ └── types/ # TypeScript 类型定义 +│ ├── 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 -提交 PR 时,请根据组件功能将其放入正确的目录: +When submitting PRs, please place components in the correct directory based on their function: -- **base**: 最基础的 UI 元素,如按钮、输入框、开关、标签等 -- **display**: 用于展示内容的组件,如卡片、列表、表格、标签页等 -- **layout**: 用于页面布局的组件,如容器、网格系统、分隔符等 -- **icons**: 所有图标相关的组件 -- **interactive**: 需要用户交互的组件,如模态框、抽屉、提示框、下拉菜单等 -- **composite**: 复合组件,由多个基础组件组合而成 +- **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 -- **总组件数**: 236 -- **已迁移**: 34 -- **已重构**: 18 -- **待迁移**: 184 +- **Total Components**: 236 +- **Migrated**: 34 +- **Refactored**: 18 +- **Pending Migration**: 184 -## 组件状态表 +## Component Status Table -| 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** | | | | 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 -### 第一阶段:复制迁移(当前阶段) +### Phase 1: Copy Migration (Current Phase) -- 将组件原样复制到 @packages/ui -- 保留原有依赖(antd、styled-components 等) -- 在文件顶部添加原路径注释 +- 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 -- 移除 antd 依赖,替换为 HeroUI -- 移除 styled-components,替换为 Tailwind CSS -- 优化组件 API 和类型定义 +- Remove antd dependencies, replace with HeroUI +- Remove styled-components, replace with Tailwind CSS +- Optimize component APIs and type definitions -## 注意事项 +## Notes -1. **不迁移**包含以下依赖的组件(解耦后可迁移): - - window.api 调用 - - Redux(useSelector、useDispatch 等) - - 其他外部数据源 +1. **Do NOT migrate** components with these dependencies (can be migrated after decoupling): + - window.api calls + - Redux (useSelector, useDispatch, etc.) + - Other external data sources -2. **可迁移**但需要后续解耦的组件: - - 使用 i18n 的组件(将 i18n 改为 props 传入) - - 使用 antd 的组件(后续替换为 HeroUI) +2. **Can migrate** but need decoupling later: + - Components using i18n (change i18n to props) + - Components using antd (replace with HeroUI later) -3. **提交规范**: - - 每次 PR 专注于一个类别的组件 - - 确保所有迁移的组件都有导出 - - 更新此文档的迁移状态 +3. **Submission Guidelines**: + - Each PR should focus on one category of components + - Ensure all migrated components are exported + - Update migration status in this document diff --git a/packages/ui/MIGRATION_STATUS_EN.md b/packages/ui/MIGRATION_STATUS_EN.md deleted file mode 100644 index 8c62e36bc7..0000000000 --- a/packages/ui/MIGRATION_STATUS_EN.md +++ /dev/null @@ -1,151 +0,0 @@ -# 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 - -```text -@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**: 34 -- **Refactored**: 18 -- **Pending Migration**: 184 - -## 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) | - -## 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 (can be migrated after decoupling): - - 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 diff --git a/packages/ui/components.json b/packages/ui/components.json index 7f39259959..693dcaf364 100644 --- a/packages/ui/components.json +++ b/packages/ui/components.json @@ -4,7 +4,7 @@ "components": "@cherrystudio/ui/components", "hooks": "@cherrystudio/ui/hooks", "lib": "@cherrystudio/ui/lib", - "ui": "@cherrystudio/ui/components/ui", + "ui": "@cherrystudio/ui/components/primitives", "utils": "@cherrystudio/ui/utils" }, "iconLibrary": "lucide", diff --git a/packages/ui/src/components/base/Button/index.tsx b/packages/ui/src/components/base/Button/index.tsx deleted file mode 100644 index 88815307aa..0000000000 --- a/packages/ui/src/components/base/Button/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { ButtonProps as HeroUIButtonProps } from '@heroui/react' -import { Button as HeroUIButton } from '@heroui/react' - -export interface ButtonProps extends HeroUIButtonProps {} - -const Button = ({ ...props }: ButtonProps) => { - return -} - -Button.displayName = 'Button' - -export default Button diff --git a/packages/ui/src/components/base/CustomCollapse/index.tsx b/packages/ui/src/components/base/CustomCollapse/index.tsx deleted file mode 100644 index d5f2261a57..0000000000 --- a/packages/ui/src/components/base/CustomCollapse/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Accordion, AccordionItem, type AccordionItemProps, type AccordionProps } from '@heroui/react' -import type { FC } from 'react' -import { memo } from 'react' - -// 重新导出 HeroUI 的组件,方便直接使用 -export { Accordion, AccordionItem } from '@heroui/react' - -interface CustomCollapseProps { - children: React.ReactNode - accordionProps?: Omit - accordionItemProps?: Omit -} - -const CustomCollapse: FC = ({ children, accordionProps = {}, accordionItemProps = {} }) => { - // 解构 Accordion 的 props - const { - defaultExpandedKeys = ['1'], - variant = 'bordered', - className = '', - isDisabled = false, - ...restAccordionProps - } = accordionProps - - // 解构 AccordionItem 的 props - const { title = 'Collapse Panel', ...restAccordionItemProps } = accordionItemProps - - return ( - - - {children} - - - ) -} - -export default memo(CustomCollapse) diff --git a/packages/ui/src/components/base/Selector/README.md b/packages/ui/src/components/base/Selector/README.md deleted file mode 100644 index 003a6f683e..0000000000 --- a/packages/ui/src/components/base/Selector/README.md +++ /dev/null @@ -1,333 +0,0 @@ -# Selector 组件 - -基于 HeroUI Select 封装的下拉选择组件,简化了 Set 和 Selection 的转换逻辑。 - -## 核心特性 - -- ✅ **类型安全**: 单选和多选自动推断回调类型 -- ✅ **智能转换**: 自动处理 `Set` 和原始值的转换 -- ✅ **HeroUI 风格**: 保持与 HeroUI 生态一致的 API -- ✅ **支持数字和字符串**: 泛型支持,自动识别值类型 - -## 基础用法 - -### 单选模式(默认) - -```tsx -import { Selector } from '@cherrystudio/ui' -import { useState } from 'react' - -function Example() { - const [language, setLanguage] = useState('zh-CN') - - const languageOptions = [ - { label: '中文', value: 'zh-CN' }, - { label: 'English', value: 'en-US' }, - { label: '日本語', value: 'ja-JP' } - ] - - return ( - { - // value 类型自动推断为 string - setLanguage(value) - }} - items={languageOptions} - placeholder="选择语言" - /> - ) -} -``` - -### 多选模式 - -```tsx -import { Selector } from '@cherrystudio/ui' -import { useState } from 'react' - -function Example() { - const [languages, setLanguages] = useState(['zh-CN', 'en-US']) - - const languageOptions = [ - { label: '中文', value: 'zh-CN' }, - { label: 'English', value: 'en-US' }, - { label: '日本語', value: 'ja-JP' }, - { label: 'Français', value: 'fr-FR' } - ] - - return ( - { - // values 类型自动推断为 string[] - setLanguages(values) - }} - items={languageOptions} - placeholder="选择语言" - /> - ) -} -``` - -### 数字类型值 - -```tsx -import { Selector } from '@cherrystudio/ui' - -function Example() { - const [priority, setPriority] = useState(1) - - const priorityOptions = [ - { label: '低', value: 1 }, - { label: '中', value: 2 }, - { label: '高', value: 3 } - ] - - return ( - - selectedKeys={priority} - onSelectionChange={(value) => { - // value 类型为 number - setPriority(value) - }} - items={priorityOptions} - /> - ) -} -``` - -### 禁用选项 - -```tsx -const options = [ - { label: '选项 1', value: '1' }, - { label: '选项 2 (禁用)', value: '2', disabled: true }, - { label: '选项 3', value: '3' } -] - - -``` - -### 自定义 Label - -```tsx -import { Flex } from '@cherrystudio/ui' - -const options = [ - { - label: ( - - 🇨🇳 - 中文 - - ), - value: 'zh-CN' - }, - { - label: ( - - 🇺🇸 - English - - ), - value: 'en-US' - } -] - - -``` - -## API - -### SelectorProps - -| 属性 | 类型 | 默认值 | 说明 | -|------|------|--------|------| -| `items` | `SelectorItem[]` | - | 必填,选项列表 | -| `selectedKeys` | `V` \| `V[]` | - | 受控的选中值(单选为单个值,多选为数组) | -| `onSelectionChange` | `(key: V) => void` \| `(keys: V[]) => void` | - | 选择变化回调(类型根据 selectionMode 自动推断) | -| `selectionMode` | `'single'` \| `'multiple'` | `'single'` | 选择模式 | -| `placeholder` | `string` | - | 占位文本 | -| `disabled` | `boolean` | `false` | 是否禁用 | -| `isRequired` | `boolean` | `false` | 是否必填 | -| `label` | `ReactNode` | - | 标签文本 | -| `description` | `ReactNode` | - | 描述文本 | -| `errorMessage` | `ReactNode` | - | 错误提示 | -| ...rest | `SelectProps` | - | 其他 HeroUI Select 属性 | - -### SelectorItem - -```tsx -interface SelectorItem { - label: string | ReactNode // 显示文本或自定义内容 - value: V // 选项值 - disabled?: boolean // 是否禁用 - [key: string]: any // 其他自定义属性 -} -``` - -## 类型安全 - -组件使用 TypeScript 条件类型,根据 `selectionMode` 自动推断回调类型: - -```tsx -// 单选模式 - ...} // v 类型: V -/> - -// 多选模式 - ...} // vs 类型: V[] -/> -``` - -## 与 HeroUI Select 的区别 - -| 特性 | HeroUI Select | Selector (本组件) | -|------|---------------|------------------| -| `selectedKeys` | `Set \| 'all'` | `V` \| `V[]` (自动转换) | -| `onSelectionChange` | `(keys: Selection) => void` | `(key: V) => void` \| `(keys: V[]) => void` | -| 单选回调 | 返回 `Set` (需手动提取) | 直接返回单个值 | -| 多选回调 | 返回 `Set` (需转数组) | 直接返回数组 | -| 类型推断 | 无 | 根据 selectionMode 自动推断 | - -## 最佳实践 - -### 1. 显式声明 selectionMode - -虽然单选是默认模式,但建议显式声明以提高代码可读性: - -```tsx -// ✅ 推荐 - - -// ⚠️ 可以但不够清晰 - -``` - -### 2. 使用泛型指定值类型 - -当值类型为数字或联合类型时,使用泛型获得更好的类型提示: - -```tsx -// ✅ 推荐 - selectedKeys={priority} ... /> - -// ✅ 推荐(联合类型) -type Status = 'pending' | 'approved' | 'rejected' - selectedKeys={status} ... /> -``` - -### 3. 避免在渲染时创建 items - -```tsx -// ❌ 不推荐(每次渲染都创建新数组) - - -// ✅ 推荐(在组件外或使用 useMemo) -const items = [{ label: 'A', value: '1' }] - -``` - -## 迁移指南 - -### 从 antd Select 迁移 - -```tsx -// antd Select -import { Select } from 'antd' - - -
-
- - -
- - -
- -