diff --git a/.github/workflows/pr-contributor-welcome.yml b/.github/workflows/pr-contributor-welcome.yml new file mode 100644 index 000000000..75dda8d01 --- /dev/null +++ b/.github/workflows/pr-contributor-welcome.yml @@ -0,0 +1,42 @@ +# 当 PR 被合并时,留言欢迎加入共建群 +name: PullRequest Contributor Welcome + +on: + pull_request_target: + types: + - closed + paths: + - 'packages/**' + +permissions: + contents: read + +jobs: + comment: + permissions: + issues: write # for actions-cool/maintain-one-comment to modify or create issue comments + pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments + if: github.event.pull_request.merged == true && github.repository == 'ant-design/x' + runs-on: ubuntu-latest + steps: + - name: get commit count + id: get_commit_count + run: | + PR_AUTHOR=$(echo "${{ github.event.pull_request.user.login }}") + RESULT_DATA=$(curl -s "https://api.github.com/repos/${{ github.repository }}/commits?author=${PR_AUTHOR}&per_page=5") + DATA_LENGTH=$(echo $RESULT_DATA | jq 'if type == "array" then length else 0 end') + echo "COUNT=$DATA_LENGTH" >> $GITHUB_OUTPUT + - name: Comment on PR + if: steps.get_commit_count.outputs.COUNT < 3 + uses: actions-cool/maintain-one-comment@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + body: | + 🎉 Thank you for your contribution! If you have not yet joined our DingTalk community group, please feel free to join us (when joining, please provide the link to this PR). + + 🎉 感谢您的贡献!如果您还没有加入钉钉社区群,请扫描下方二维码加入我们(加群时请提供此 PR 链接)。 + + Thank you for your contribution! + + + body-include: diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index 516ad4a79..befc2084a 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -29,7 +29,7 @@ jobs: env: NODE_OPTIONS: '--max_old_space_size=4096' - name: upload site artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: site path: packages/x/_site/ @@ -41,7 +41,7 @@ jobs: - name: Upload PR number if: ${{ always() }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: pr path: ./pr-id.txt diff --git a/.github/workflows/release-dingtalk.yml b/.github/workflows/release-dingtalk.yml index 0557c36f9..e12d10545 100644 --- a/.github/workflows/release-dingtalk.yml +++ b/.github/workflows/release-dingtalk.yml @@ -18,9 +18,9 @@ jobs: trigger: tag changelogs: 'CHANGELOG.en-US.md, CHANGELOG.zh-CN.md' branch: 'main' - tag: '1*' - latest: '1*' - dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }} + tag: '2*' + latest: '2*' + dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }} ${{ secrets.DINGDING_BOT_INTERNAL_TOKEN }} ${{ secrets.DINGDING_BOT_XMARKDOWN_TOKEN }} dingding-msg: CHANGELOG.zh-CN.md msg-title: '# Ant Design X {{v}} 发布日志' msg-poster: 'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*kjHUSYIdsnUAAAAAAAAAAAAADgCCAQ/original' diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 85b615270..7c62a31d3 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -16,6 +16,58 @@ tag: vVERSION --- +## 2.1.1 + +`2025-12-10` + +### @ant-design/x + +- Sender + - 🐛 Fixed the issue where send shortcuts enter and shift + enter were not controlled by the disabled state of the submit button, and fixed the inconsistency between `onSubmit` shortcut keys and button parameters. [#1472](https://github.com/ant-design/x/pull/1472) by [@kimteayon](https://github.com/kimteayon) + - 🐛 Fixed the missing `skill` parameter in `onChange`, fixed the issue where placeholder was not displayed when slot mode only showed skill capabilities, and refactored `onChange` logic. [#1477](https://github.com/ant-design/x/pull/1477) by [@kimteayon](https://github.com/kimteayon) + - 🐛 Fixed the issue where send shortcuts enter and shift + enter were not triggered when `input` type slot was activated and focused in slot mode. [#1498](https://github.com/ant-design/x/pull/1498) by [@kimteayon](https://github.com/kimteayon) +- Attachment + - 🐛 Fixed the issue where the last file was not uploaded after setting `maxCount`. [#1486](https://github.com/ant-design/x/pull/1486) by [@kimteayon](https://github.com/kimteayon) + - 🐛 Fixed the antd warning issue after uploading images. [#1492](https://github.com/ant-design/x/pull/1492) by [@kimteayon](https://github.com/kimteayon) +- 🐛 Fixed Mermaid rendering jitter issue. [#1497](https://github.com/ant-design/x/pull/1497) by [@Div627](https://github.com/Div627) +- 📖 Optimized official website to improve user experience. [#1464](https://github.com/ant-design/x/pull/1464) by [@IsDyh01](https://github.com/IsDyh01), [#1483](https://github.com/ant-design/x/pull/1483) by [@Chiaki-xps](https://github.com/Chiaki-xps), [#1463](https://github.com/ant-design/x/pull/1463) by [@J-Da-Shi](https://github.com/J-Da-Shi), [#1489](https://github.com/ant-design/x/pull/1489) by [@Chiaki-xps](https://github.com/Chiaki-xps), [#1499](https://github.com/ant-design/x/pull/1499) by [@kimteayon](https://github.com/kimteayon), [#1500](https://github.com/ant-design/x/pull/1500) by [@kimteayon](https://github.com/kimteayon), [#1501](https://github.com/ant-design/x/pull/1501) by [@Samoy](https://github.com/Samoy) +- 🛠 Modified dependency configuration for `mermaid`. [#1475](https://github.com/ant-design/x/pull/1475) by [@Div627](https://github.com/Div627) + +### @ant-design/x-sdk + +- 🐛 Optimized message flow throttling and emission logic to avoid deep update errors caused by high-frequency streaming updates, improving real-time message stability and performance. [#1418](https://github.com/ant-design/x/pull/1418) by [@Afee2019](https://github.com/Afee2019) + +### @ant-design/x-markdown + +- 🛠 Optimized `sideEffects` configuration. [#1408](https://github.com/ant-design/x/pull/1408) by [@hongxuWei](https://github.com/hongxuWei) + +## 2.1.0 + +`2025-12-05` + +### @ant-design/x + +- 🐛 Fixed Bubble css token `typingContent` configuration not taking effect. [#1435](https://github.com/ant-design/x/pull/1435) by [@kimteayon](https://github.com/kimteayon) +- 🐛 Fixed multiple component style loss issues caused by antd upgrade to 6.0.1. [#1441](https://github.com/ant-design/x/pull/1441) by [@kimteayon](https://github.com/kimteayon), [#1446](https://github.com/ant-design/x/pull/1446) by [@kimteayon](https://github.com/kimteayon) +- 🐛 Fixed Bubble.List scrolling compatibility issue in Safari browser. [#1392](https://github.com/ant-design/x/pull/1392) by [@anxLiang](https://github.com/anxLiang) +- 🔥 New components HighlightCode and Mermaid. [#1402](https://github.com/ant-design/x/pull/1402) by [@Div627](https://github.com/Div627) +- 🆕 Added semantic implementation for Actions. [#1443](https://github.com/ant-design/x/pull/1443) by [@kimteayon](https://github.com/kimteayon) +- 🆕 Added semantic implementation for Suggestion, removed duplicate Enter trigger events, fixed the issue of `onSubmit` method being executed multiple times, added complete data return of `selectedOptions` to `onSelect` method, and refactored the option implementation using `useMergedState`. [#1406](https://github.com/ant-design/x/pull/1406) by [@kimteayon](https://github.com/kimteayon) +- 📖 Optimized official website to improve user experience. [#1444](https://github.com/ant-design/x/pull/1444) by [@kimteayon](https://github.com/kimteayon) +- 🆕 Added new slot type `content` and skill function `skill` for Sender. [#1377](https://github.com/ant-design/x/pull/1377) by [@kimteayon](https://github.com/kimteayon) + +### @ant-design/x-sdk + +- 🐛 Fixed DeepSeekChatProvider's improper handling of newline characters in `` tag format causing XMarkdown rendering anomalies. [#1445](https://github.com/ant-design/x/pull/1445) by [@kimteayon](https://github.com/kimteayon) +- 🐛 Fixed useXChat `setMessages` method call not triggering re-rendering. [#1450](https://github.com/ant-design/x/pull/1450) by [@hylin](https://github.com/hylin) +- 🐛 Fixed missing rc-util dependency declaration. [#1456](https://github.com/ant-design/x/pull/1456) by [@hylin](https://github.com/hylin) + +### @ant-design/x-markdown + +- 🐛 Replaced useStreaming regex to resolve iOS compatibility issues. [#1457](https://github.com/ant-design/x/pull/1457) by [@Div627](https://github.com/Div627) +- 📖 Improved documentation to enhance user experience. [#1451](https://github.com/ant-design/x/pull/1451) by [@Div627](https://github.com/Div627) +- 🛠 Migrated UI plugins HighlightCode and Mermaid to @ant-design/x to achieve more reasonable dependency relationships. [#1402](https://github.com/ant-design/x/pull/1402) by [@Div627](https://github.com/Div627) + ## 2.0.1 `2025-12-03` diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 3bdeeb25b..ce98c5761 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -15,6 +15,58 @@ tag: vVERSION --- +## 2.1.1 + +`2025-12-10` + +### @ant-design/x + +- Sender + - 🐛 修复发送快捷键 enter 和 shift + enter 未受 submit 按钮 disabled 状态控制的问题,修复 `onSubmit` 快捷键和按钮参数不一致问题。 [#1472](https://github.com/ant-design/x/pull/1472) 由 [@kimteayon](https://github.com/kimteayon) 提交 + - 🐛 修复 `onChange` 缺少 `skill` 参数问题,修复词槽模式仅展示技能能力时 placeholder 未展示问题,并重构 `onChange`逻辑。[#1477](https://github.com/ant-design/x/pull/1477) 由 [@kimteayon](https://github.com/kimteayon) 提交 + - 🐛 修复词槽模式 `input` 类型词槽激活焦点时发送快捷键 enter 和 shift + enter 未触发问题。[#1498](https://github.com/ant-design/x/pull/1498) 由 [@kimteayon](https://github.com/kimteayon) 提交 +- Attachment + - 🐛 修复设置 `maxCount` 后最后一个文件未上传问题。[#1486](https://github.com/ant-design/x/pull/1486) 由 [@kimteayon](https://github.com/kimteayon) 提交 + - 🐛 修复上传图片后 antd 报警告问题。[#1492](https://github.com/ant-design/x/pull/1492) 由 [@kimteayon](https://github.com/kimteayon) 提交 +- 🐛 修复 Mermaid 渲染抖动问题。[#1497](https://github.com/ant-design/x/pull/1497) 由 [@Div627](https://github.com/Div627) 提交 +- 📖 优化官网站点提升用户体验。[#1464](https://github.com/ant-design/x/pull/1464) 由 [@IsDyh01](https://github.com/IsDyh01) 提交、[#1483](https://github.com/ant-design/x/pull/1483) 由 [@Chiaki-xps](https://github.com/Chiaki-xps) 提交、[#1463](https://github.com/ant-design/x/pull/1463) 由 [@J-Da-Shi](https://github.com/J-Da-Shi) 提交、[#1489](https://github.com/ant-design/x/pull/1489) 由 [@Chiaki-xps](https://github.com/Chiaki-xps) 提交、[#1499](https://github.com/ant-design/x/pull/1499) 由 [@kimteayon](https://github.com/kimteayon) 提交、[#1500](https://github.com/ant-design/x/pull/1500) 由 [@kimteayon](https://github.com/kimteayon) 提交、[#1501](https://github.com/ant-design/x/pull/1501) 由 [@Samoy](https://github.com/Samoy) 提交 +- 🛠 修改对 `mermaid` 的依赖配置。[#1475](https://github.com/ant-design/x/pull/1475) 由 [@Div627](https://github.com/Div627) 提交 + +### @ant-design/x-sdk + +- 🐛 优化消息流的节流与发射逻辑,避免高频流式更新导致的深度更新错误,提升实时消息稳定性与性能。[#1418](https://github.com/ant-design/x/pull/1418) 由 [@Afee2019](https://github.com/Afee2019) 提交 + +### @ant-design/x-markdown + +- 🛠 优化 `sideEffects` 配置。[#1408](https://github.com/ant-design/x/pull/1408) 由 [@hongxuWei](https://github.com/hongxuWei) 提交 + +## 2.1.0 + +`2025-12-05` + +### @ant-design/x + +- 🐛 修复 Bubble css token `typingContent` 配置不生效问题。[#1435](https://github.com/ant-design/x/pull/1435) 由 [@kimteayon](https://github.com/kimteayon) 提交 +- 🐛 修复因 antd 升级到 6.0.1 导致多个组件样式丢失问题。[#1441](https://github.com/ant-design/x/pull/1441) 由 [@kimteayon](https://github.com/kimteayon) 提交、[#1446](https://github.com/ant-design/x/pull/1446) 由 [@kimteayon](https://github.com/kimteayon) 提交 +- 🐛 修复 Bubble.List 在 safari 浏览器滚动兼容性问题。[#1392](https://github.com/ant-design/x/pull/1392) 由 [@anxLiang](https://github.com/anxLiang) 提交 +- 🔥 新组件 HighlightCode 和 Mermaid。[#1402](https://github.com/ant-design/x/pull/1402) 由 [@Div627](https://github.com/Div627) 提交 +- 🆕 Actions 新增语义化实现。[#1443](https://github.com/ant-design/x/pull/1443) 由 [@kimteayon](https://github.com/kimteayon) 提交 +- 🆕 Suggestion 新增语义化实现,移除重复的 Enter 触发事件,修复 `onSubmit` 方法多次执行的问题,`onSelect` 方法新增 `selectedOptions` 完整数据返回,同时对选项的实现使用 `useMergedState` 进行了重构。[#1406](https://github.com/ant-design/x/pull/1406) 由 [@kimteayon](https://github.com/kimteayon) 提交 +- 📖 优化官网站点提升用户体验。[#1444](https://github.com/ant-design/x/pull/1444) 由 [@kimteayon](https://github.com/kimteayon) 提交 +- 🆕 Sender 新的词槽类型 `content` 和技能功能 `skill`。[#1377](https://github.com/ant-design/x/pull/1377) 由 [@kimteayon](https://github.com/kimteayon) 提交 + +### @ant-design/x-sdk + +- 🐛 修复 DeepSeekChatProvider 对 `` 标签格式换行处理不当导致 XMarkdown 格式渲染异常问题。[#1445](https://github.com/ant-design/x/pull/1445) 由 [@kimteayon](https://github.com/kimteayon) 提交 +- 🐛 修复 useXChat `setMessages` 方法调用未触发渲染问题。[#1450](https://github.com/ant-design/x/pull/1450) 由 [@hylin](https://github.com/hylin) 提交 +- 🐛 修复 rc-util 依赖未声明问题。[#1456](https://github.com/ant-design/x/pull/1456) 由 [@hylin](https://github.com/hylin) 提交 + +### @ant-design/x-markdown + +- 🐛 替换 useStreaming 正则解决 ios 兼容性问题。[#1457](https://github.com/ant-design/x/pull/1457) 由 [@Div627](https://github.com/Div627) 提交 +- 📖 完善文档提升用户体验。[#1451](https://github.com/ant-design/x/pull/1451) 由 [@Div627](https://github.com/Div627) 提交 +- 🛠 迁移 UI 插件 HighlightCode 和 Mermaid 到 @ant-design/x 达成更合理的依赖关系。[#1402](https://github.com/ant-design/x/pull/1402) 由 [@Div627](https://github.com/Div627) 提交 + ## 2.0.1 `2025-12-03` diff --git a/package.json b/package.json index 7c7364018..6604f1ab8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "x-mono", - "version": "2.0.1", + "version": "2.1.1", "private": true, "scripts": { "presite": "npm run prestart --workspaces", @@ -15,7 +15,7 @@ "publish-version": "tsx ./scripts/synchronize-version.ts", "prepublishOnly": "tsx ./scripts/pre-publish.ts", "precompile": "npm run precompile --workspaces", - "prepare": "husky install", + "prepare": "husky", "size-limit": "size-limit", "lint": "npm run lint --workspaces", "tsc": "npm run tsc --workspaces", @@ -29,27 +29,27 @@ "packages/*" ], "devDependencies": { - "@ant-design/tools": "^19.0.2", + "@ant-design/tools": "^19.1.0", "@biomejs/biome": "^2.0.5", "@codecov/webpack-plugin": "^1.4.0", "@madccc/duplicate-package-checker-webpack-plugin": "^1.0.0", "@size-limit/file": "^11.1.5", - "antd": "^6.0.1", + "antd": "^6.1.0", "circular-dependency-plugin": "^5.2.2", "dekko": "^0.2.1", "father": "^4.6.0", + "gh-pages": "^6.3.0", "husky": "^9.1.6", "lint-staged": "^16.1.5", - "gh-pages": "^6.3.0", "marked-emoji": "^2.0.1", "prettier": "^3.3.3", "react-markdown": "^10.1.0", - "size-limit": "^11.1.5" + "size-limit": "^12.0.0" }, "size-limit": [ { "path": "./packages/x/dist/antdx.min.js", - "limit": "350 KiB" + "limit": "500 KiB" }, { "path": "./packages/x-sdk/dist/x-sdk.min.js", @@ -57,19 +57,11 @@ }, { "path": "./packages/x-markdown/dist/x-markdown.min.js", - "limit": "350 KiB" - }, - { - "path": "./packages/x-markdown/dist/plugins/code-high-light.min.js", - "limit": "700 KiB" + "limit": "150 KiB" }, { "path": "./packages/x-markdown/dist/plugins/latex.min.js", - "limit": "700 KiB" - }, - { - "path": "./packages/x-markdown/dist/plugins/mermaid.min.js", - "limit": "900 KiB" + "limit": "300 KiB" } ], "description": "Craft AI-driven interfaces effortlessly", @@ -89,4 +81,4 @@ "@emotion/hash": "^0.9.2", "react-is": "^18.3.1" } -} \ No newline at end of file +} diff --git a/packages/x-markdown/.fatherrc.ts b/packages/x-markdown/.fatherrc.ts index f18bddfa1..8b509b875 100644 --- a/packages/x-markdown/.fatherrc.ts +++ b/packages/x-markdown/.fatherrc.ts @@ -46,15 +46,6 @@ export default defineConfig({ filename: 'x-markdown', }, }, - 'src/plugins/HighlightCode/index.tsx': { - name: 'HighlightCode', - sourcemap: true, - generateUnminified: true, - output: { - path: 'dist/plugins', - filename: 'code-high-light', - }, - }, 'src/plugins/Latex/index.ts': { name: 'Latex', sourcemap: true, @@ -64,15 +55,6 @@ export default defineConfig({ filename: 'latex', }, }, - 'src/plugins/Mermaid/index.tsx': { - name: 'Mermaid', - sourcemap: true, - generateUnminified: true, - output: { - path: 'dist/plugins', - filename: 'mermaid', - }, - }, }, bundler: 'webpack', // bundler: 'utoopack', @@ -89,17 +71,7 @@ export default defineConfig({ commonjs: 'react-dom', commonjs2: 'react-dom', }, - '@ant-design/cssinjs': { - root: 'antdCssinjs', - commonjs: 'antdCssinjs', - commonjs2: 'antdCssinjs', - }, }, - // externals: { - // react: 'React', - // 'react-dom': 'ReactDOM', - // '@ant-design/cssinjs': 'antdCssinjs', - // }, transformRuntime: { absoluteRuntime: process.cwd(), }, @@ -116,26 +88,12 @@ export default defineConfig({ memo.plugin('circular-dependency-checker').use(CircularDependencyPlugin, [ { failOnError: true, - exclude: /node_modules[\\/](chevrotain|d3-.*|langium)/, }, ]); memo.plugin('duplicate-package-checker').use(DuplicatePackageCheckerPlugin, [ { verbose: true, emitError: true, - exclude: (instance: any) => { - // 排除特定包 - if ( - instance.name === 'cose-base' || - instance.name === 'layout-base' || - instance.name.startsWith('d3-') || - instance.name === 'internmap' - ) { - return true; - } - - return false; - }, }, ]); } diff --git a/packages/x-markdown/.jest.js b/packages/x-markdown/.jest.js index 368774c23..7d7f3cfd8 100644 --- a/packages/x-markdown/.jest.js +++ b/packages/x-markdown/.jest.js @@ -1,4 +1,5 @@ const compileModules = [ + '@rc-component', 'react-sticky-box', 'rc-tween-one', '@babel', diff --git a/packages/x-markdown/package.json b/packages/x-markdown/package.json index 79f4b9a72..37e632c9b 100644 --- a/packages/x-markdown/package.json +++ b/packages/x-markdown/package.json @@ -1,6 +1,6 @@ { "name": "@ant-design/x-markdown", - "version": "2.0.1", + "version": "2.1.1", "scripts": { "compile": "father build", "tsc": "tsc --noEmit", @@ -13,16 +13,16 @@ "test": "jest --config .jest.js --no-cache --collect-coverage", "coverage": "jest --config .jest.js --no-cache --collect-coverage --coverage", "plugin:meta": "tsx scripts/generate-plugin-meta.ts", - "prestart": "npm run version && npm run token:statistic && npm run plugin:meta && npm run token:meta", + "prestart": "npm run version && npm run plugin:meta", "precompile": "npm run prestart", "version": "tsx scripts/generate-version.ts", "test:dekko": "tsx ./tests/dekko/index.test.ts", "clean": "rm -rf es lib coverage plugins dist themes", - "test:package-diff": "antd-tools run package-diff", - "token:meta": "tsx scripts/generate-token-meta.ts", - "token:statistic": "tsx scripts/collect-token-statistic.ts" + "test:package-diff": "antd-tools run package-diff" }, - "sideEffects": false, + "sideEffects": [ + "**/*.css" + ], "main": "lib/index.js", "module": "es/index.js", "typings": "es/index.d.ts", @@ -49,19 +49,11 @@ "license": "MIT", "description": "placeholder for @ant-design/x-markdown", "dependencies": { - "@ant-design/cssinjs": "^2.0.1", - "@ant-design/cssinjs-utils": "^2.0.2", - "@ant-design/fast-color": "^3.0.0", - "@ant-design/icons": "^6.0.0", - "@ant-design/x": "*", - "classnames": "^2.5.1", + "clsx": "^2.1.1", "dompurify": "^3.2.6", "html-react-parser": "^5.2.5", "katex": "^0.16.22", - "lodash.throttle": "^4.1.1", - "marked": "^15.0.12", - "mermaid": "^11.11.0", - "react-syntax-highlighter": "^15.6.1" + "marked": "^15.0.12" }, "devDependencies": { "@types/dompurify": "^3.0.5", @@ -70,13 +62,12 @@ "@types/react-dom": "^19.0.2", "@types/react-syntax-highlighter": "^15.5.13", "@umijs/mako": "^0.11.10", - "antd": "^6.0.1", + "antd": "^6.1.0", "glob": "^11.0.0", "react": "^19.0.0", "react-dom": "^19.0.0" }, "peerDependencies": { - "antd": "^6.0.1", "react": ">=18.0.0", "react-dom": ">=18.0.0" }, diff --git a/packages/x-markdown/scripts/collect-token-statistic.ts b/packages/x-markdown/scripts/collect-token-statistic.ts deleted file mode 100644 index 5d011adfa..000000000 --- a/packages/x-markdown/scripts/collect-token-statistic.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* eslint-disable no-console */ - -import { statistic } from '@ant-design/cssinjs-utils'; -import { theme } from 'antd'; -import chalk from 'chalk'; -import cliProgress from 'cli-progress'; -import fs from 'fs-extra'; -import React from 'react'; -import ReactDOMServer from 'react-dom/server'; -import { generateCssinjs, styleFiles } from './generate-cssinjs'; - -console.log(`🪄 Collecting token statistics...`); - -const bar = new cliProgress.SingleBar( - { - format: `🪄 Collecting by component: [${chalk.cyan('{bar}')}] {component} | {value}/{total}`, - }, - cliProgress.Presets.rect, -); - -bar.start(styleFiles.length, 0); - -(async () => { - await generateCssinjs({ - key: 'file', - beforeRender(componentName: string) { - bar.increment({ component: componentName }); - }, - render(Component: any) { - ReactDOMServer.renderToString(React.createElement(Component)); - // Render wireframe - const wireframeToken = { ...theme.defaultConfig.token, wireframe: true }; - ReactDOMServer.renderToString( - React.createElement( - theme._internalContext.Provider, - { - value: { - token: wireframeToken, - override: { - override: wireframeToken, - }, - }, - }, - React.createElement(Component), - ), - ); - }, - }); - bar.stop(); - const tokenPath = `${process.cwd()}/src/plugins/version/token.json`; - fs.writeJsonSync(tokenPath, statistic, 'utf8'); - console.log(chalk.green(`✅ Collected token statistics successfully, check it in`), tokenPath); -})(); diff --git a/packages/x-markdown/scripts/generate-cssinjs.ts b/packages/x-markdown/scripts/generate-cssinjs.ts deleted file mode 100644 index 24a2bcd3b..000000000 --- a/packages/x-markdown/scripts/generate-cssinjs.ts +++ /dev/null @@ -1,48 +0,0 @@ -import url from 'node:url'; -import { globSync } from 'glob'; -import path from 'path'; -import React from 'react'; - -type StyleFn = (prefix?: string) => void; - -interface GenCssinjsOptions { - key: string; - render: (component: React.FC) => void; - beforeRender?: (componentName: string) => void; -} - -export const styleFiles = globSync( - path - .join( - process.cwd(), - 'src/plugins/!(version|config-provider|icon|auto-complete|col|row|time-picker|qrcode)/style/index.?(ts|tsx)', - ) - .split(path.sep) - .join('/'), -); - -export const generateCssinjs = ({ key, beforeRender, render }: GenCssinjsOptions) => - Promise.all( - styleFiles.map(async (file) => { - const absPath = url.pathToFileURL(file).href; - const pathArr = file.split('/'); - const styleIndex = pathArr.lastIndexOf('style'); - const componentName = pathArr[styleIndex - 1]; - let useStyle: StyleFn = () => {}; - if (file.includes('grid')) { - const { useColStyle, useRowStyle } = await import(absPath); - useStyle = (prefixCls) => { - useRowStyle(prefixCls); - useColStyle(prefixCls); - }; - } else { - useStyle = (await import(absPath)).default; - } - const Demo: React.FC = () => { - useStyle(`${key}-${componentName}`); - return React.createElement('div'); - }; - beforeRender?.(componentName); - render?.(Demo); - }), - ); diff --git a/packages/x-markdown/scripts/generate-plugin-meta.ts b/packages/x-markdown/scripts/generate-plugin-meta.ts index 12c404277..4bba6c2c9 100644 --- a/packages/x-markdown/scripts/generate-plugin-meta.ts +++ b/packages/x-markdown/scripts/generate-plugin-meta.ts @@ -17,6 +17,7 @@ function getPluginMeta(list?: DeclarationReflection[]) { }; }); } + const main = async () => { const app = await (Application as any).bootstrap( { diff --git a/packages/x-markdown/scripts/generate-token-meta.ts b/packages/x-markdown/scripts/generate-token-meta.ts deleted file mode 100644 index 5dd904073..000000000 --- a/packages/x-markdown/scripts/generate-token-meta.ts +++ /dev/null @@ -1,144 +0,0 @@ -import fs from 'fs-extra'; -import type { DeclarationReflection } from 'typedoc'; -import { Application, TSConfigReader, TypeDocReader } from 'typedoc'; - -interface TokenMeta { - seed: ReturnType; - map: ReturnType; - alias: ReturnType; - components: Record>; -} - -function getTokenList(list?: DeclarationReflection[], source?: string) { - return (list || []) - .filter( - (item) => - !item.comment?.blockTags.some( - (tag) => tag.tag === '@internal' || tag.tag === '@private' || tag.tag === '@deprecated', - ), - ) - .map((item) => ({ - source, - token: item.name, - type: item?.type?.toString(), - desc: - item.comment?.blockTags - ?.find((tag) => tag.tag === '@desc') - ?.content.reduce((result, str) => result.concat(str.text), '') || '', - descEn: - item.comment?.blockTags - ?.find((tag) => tag.tag === '@descEN') - ?.content.reduce((result, str) => result.concat(str.text), '') || '', - name: - item.comment?.blockTags - ?.find((tag) => tag.tag === '@nameZH') - ?.content.reduce((result, str) => result.concat(str.text), '') || '', - nameEn: - item.comment?.blockTags - ?.find((tag) => tag.tag === '@nameEN') - ?.content.reduce((result, str) => result.concat(str.text), '') || '', - })); -} - -const main = async () => { - const app = await (Application as any).bootstrap( - { - // typedoc options here - entryPoints: [ - 'src/plugins/theme/interface/index.ts', - 'src/plugins/theme/interface/components.ts', - ], - skipErrorChecking: true, - logLevel: 'Error', - }, - [new TSConfigReader(), new TypeDocReader()], - ); - - const project = await app.convert(); - - if (project) { - // Project may not have converted correctly - const output = 'src/plugins/version/token-meta.json'; - const tokenMeta: TokenMeta = { - seed: [], - map: [], - alias: [], - components: {}, - }; - - // eslint-disable-next-line no-restricted-syntax - project?.children?.forEach((file: any) => { - // Global Token - if (file.name === 'index') { - let presetColors: string[] = []; - - file.children?.forEach((type: any) => { - if (type.name === 'SeedToken') { - tokenMeta.seed = getTokenList(type.children, 'seed'); - } else if (type.name === 'MapToken') { - tokenMeta.map = getTokenList(type.children, 'map'); - } else if (type.name === 'AliasToken') { - tokenMeta.alias = getTokenList(type.children, 'alias'); - } else if (type.name === 'PresetColors') { - presetColors = (type?.type as any)?.target?.elements?.map((item: any) => item.value); - } - }); - - // Exclude preset colors - tokenMeta.seed = tokenMeta.seed.filter( - (item) => !presetColors.some((color) => item.token.startsWith(color)), - ); - tokenMeta.map = tokenMeta.map.filter( - (item) => !presetColors.some((color) => item.token.startsWith(color)), - ); - tokenMeta.alias = tokenMeta.alias.filter( - (item) => !presetColors.some((color) => item.token.startsWith(color)), - ); - - tokenMeta.alias = tokenMeta.alias.filter( - (item) => !tokenMeta.map.some((mapItem) => mapItem.token === item.token), - ); - tokenMeta.map = tokenMeta.map.filter( - (item) => !tokenMeta.seed.some((seedItem) => seedItem.token === item.token), - ); - } else { - const componentTokens = file.children?.filter((item: any) => - item?.name?.includes('PluginToken'), - ); - componentTokens.forEach( - (componentToken: { name: string; children: DeclarationReflection[] | undefined }) => { - const component = componentToken.name.replace('PluginToken', ''); - if (componentToken) { - tokenMeta.components[component] = getTokenList(componentToken.children, component); - } - }, - ); - } - }); - - const finalMeta = Object.entries(tokenMeta).reduce((acc, [key, value]) => { - if (key !== 'components') { - (value as any[]).forEach((item) => { - acc.global = acc.global || {}; - acc.global[item.token] = { - name: item.name, - nameEn: item.nameEn, - desc: item.desc, - descEn: item.descEn, - type: item.type, - source: key, - }; - }); - } else { - acc.components = value; - } - return acc; - }, {} as any); - - fs.writeJsonSync(output, finalMeta, 'utf8'); - // eslint-disable-next-line no-console - console.log(`✅ Token Meta has been written to ${output}`); - } -}; - -main(); diff --git a/packages/x-markdown/src/XMarkdown/__test__/__snapshots__/index.test.tsx.snap b/packages/x-markdown/src/XMarkdown/__test__/__snapshots__/index.test.tsx.snap index c1efa6868..21109cd0f 100644 --- a/packages/x-markdown/src/XMarkdown/__test__/__snapshots__/index.test.tsx.snap +++ b/packages/x-markdown/src/XMarkdown/__test__/__snapshots__/index.test.tsx.snap @@ -3,8 +3,7 @@ exports[`XMarkdown animation parent is custom components 1`] = `

This is Text. @@ -16,8 +15,7 @@ exports[`XMarkdown animation parent is custom components 1`] = ` exports[`XMarkdown animation parent is not custom components 1`] = `

  • @@ -53,8 +50,7 @@ exports[`XMarkdown support checkbox is checked 1`] = ` exports[`XMarkdown support checkbox not checked 1`] = `
    • diff --git a/packages/x-markdown/src/XMarkdown/__test__/index.test.tsx b/packages/x-markdown/src/XMarkdown/__test__/index.test.tsx index 461807130..fdc8b6df0 100644 --- a/packages/x-markdown/src/XMarkdown/__test__/index.test.tsx +++ b/packages/x-markdown/src/XMarkdown/__test__/index.test.tsx @@ -194,7 +194,7 @@ describe('XMarkdown', () => { // XMarkdown wraps content in a div with class "ant-x-markdown" const wrapper = container.firstChild as HTMLElement; - expect(wrapper).toHaveClass('ant-x-markdown'); + expect(wrapper).toHaveClass('x-markdown'); expect(wrapper.innerHTML).toBe('
      This is a paragraph.
      \n'); }); diff --git a/packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts b/packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts index ce617c4c7..31d74bae0 100644 --- a/packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts +++ b/packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts @@ -158,18 +158,40 @@ const isInCodeBlock = (text: string): boolean => { return inFenced; }; +const sanitizeForURIComponent = (input: string): string => { + let result = ''; + for (let i = 0; i < input.length; i++) { + const charCode = input.charCodeAt(i); + + // 处理代理对:保留合法,跳过孤立 + if (charCode >= 0xd800 && charCode <= 0xdbff) { + // High surrogate + // Check for a following low surrogate to form a valid pair + if ( + i + 1 < input.length && + input.charCodeAt(i + 1) >= 0xdc00 && + input.charCodeAt(i + 1) <= 0xdfff + ) { + result += input[i] + input[i + 1]; + i++; // Skip the low surrogate as it's already processed + } + // Lone high surrogates are otherwise skipped + } else if (charCode < 0xdc00 || charCode > 0xdfff) { + // Append characters that are not lone low surrogates + result += input[i]; + } + // Lone low surrogates are otherwise skipped + } + return result; +}; + const safeEncodeURIComponent = (str: string): string => { try { return encodeURIComponent(str); } catch (e) { if (e instanceof URIError) { - const cleanedStr = str.replace( - /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(? = React.memo((props) => { content, children, rootClassName, - prefixCls: customizePrefixCls, className, style, openLinksInNewTab, @@ -23,19 +21,7 @@ const XMarkdown: React.FC = React.memo((props) => { } = props; // ============================ style ============================ - const { direction: contextDirection, getPrefixCls } = useXProviderContext(); - - const prefixCls = getPrefixCls('x-markdown', customizePrefixCls); - - const mergedCls = classnames(prefixCls, 'x-markdown', rootClassName, className); - - const mergedStyle: React.CSSProperties = useMemo( - () => ({ - direction: contextDirection === 'rtl' ? 'rtl' : 'ltr', - ...style, - }), - [contextDirection, style], - ); + const mergedCls = clsx('x-markdown', rootClassName, className); // ============================ Streaming ============================ const displayContent = useStreaming(content || children || '', { streaming, components }); @@ -69,7 +55,7 @@ const XMarkdown: React.FC = React.memo((props) => { if (!displayContent) return null; return ( -
      +
      {renderer.render(htmlString)}
      ); diff --git a/packages/x-markdown/src/hooks/use-x-provider-context.ts b/packages/x-markdown/src/hooks/use-x-provider-context.ts deleted file mode 100644 index 97adbfb0a..000000000 --- a/packages/x-markdown/src/hooks/use-x-provider-context.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ConfigProvider } from 'antd'; -import React from 'react'; - -export const defaultPrefixCls = 'ant'; - -function useXProviderContext() { - const { getPrefixCls, direction, csp, iconPrefixCls, theme } = React.useContext( - ConfigProvider.ConfigContext, - ); - - return { - theme, - getPrefixCls, - direction, - csp, - iconPrefixCls, - }; -} - -export default useXProviderContext; diff --git a/packages/x-markdown/src/plugins/HighlightCode/__test__/index.test.tsx b/packages/x-markdown/src/plugins/HighlightCode/__test__/index.test.tsx deleted file mode 100644 index b2eff1bb1..000000000 --- a/packages/x-markdown/src/plugins/HighlightCode/__test__/index.test.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { render } from '@testing-library/react'; -import React from 'react'; -import XMarkdown from '../../../XMarkdown'; -import HighlightCode from '..'; - -jest.mock('react', () => ({ - ...jest.requireActual('react'), - useId: () => 'mock-id-123', -})); - -describe('HighlightCode', () => { - it('render normal code', () => { - const content = `\`\`\`javascript -console.log("javascript"); -\`\`\``; - - const { container } = render( - ) => { - const { class: className, children } = props; - const lang = className?.match(/language-(\w+)/)?.[1] || ''; - return {children}; - }, - }} - />, - ); - expect(container.querySelector('pre')).toBeInTheDocument(); - expect(container.querySelector('code')).toBeInTheDocument(); - expect(container.textContent).toContain('console.log("javascript");'); - }); - - it('render normal code with header', () => { - const content = `\`\`\`javascript -console.log("javascript"); -\`\`\``; - - const { container } = render( - ) => { - const { class: className, children } = props; - const lang = className?.match(/language-(\w+)/)?.[1] || ''; - return {children}; - }, - }} - />, - ); - expect(container.querySelector('.ant-highlightCode-header')).toBeInTheDocument(); - expect(container.querySelector('.ant-highlightCode-header')?.textContent).toContain( - 'javascript', - ); - }); - - it('render normal code with custom header class', () => { - const content = `\`\`\`javascript -console.log("javascript"); -\`\`\``; - - const { container } = render( - ) => { - const { class: className, children } = props; - const lang = className?.match(/language-(\w+)/)?.[1] || ''; - return ( - - {children} - - ); - }, - }} - />, - ); - expect(container.querySelector('.customHeader')).toBeInTheDocument(); - }); - - it('render normal code with custom header', () => { - const content = `\`\`\`javascript -console.log("javascript"); -\`\`\``; - - const { container } = render( - ) => { - const { class: className, children } = props; - const lang = className?.match(/language-(\w+)/)?.[1] || ''; - return ( - custom header
      } - > - {children} - - ); - }, - }} - />, - ); - expect(container.querySelector('.myCustomClass')).toBeInTheDocument(); - expect(container.querySelector('.myCustomClass')?.textContent).toContain('custom header'); - }); - - it('render normal code with no header', () => { - const content = `\`\`\`javascript -console.log("javascript"); -\`\`\``; - - const { container } = render( - ) => { - const { class: className, children } = props; - const lang = className?.match(/language-(\w+)/)?.[1] || ''; - return ( - - {children} - - ); - }, - }} - />, - ); - expect(container.querySelector('.ant-highlightCode-header')).toBeNull(); - }); - - it('render normal code with no children', () => { - const { container } = render({''}); - expect(container.querySelector('code')).toBeNull(); - }); - - it('mermaid code is render as text', () => { - const content = `\`\`\`mermaid -graph TD; A-->B; -\`\`\``; - - const { container } = render( - ) => { - const { class: className, children } = props; - const lang = className?.match(/language-(\w+)/)?.[1] || ''; - return {children}; - }, - }} - />, - ); - expect(container.querySelector('pre')).toBeInTheDocument(); - expect(container.textContent).toContain('graph TD; A-->B;'); - }); - - it('should handle undefined lang', () => { - const content = ` - \`\`\` -plain text -\`\`\``; - - const { container } = render( - ) => { - const { className, children } = props; - const lang = className?.match(/language-(\w+)/)?.[1] || ''; - return {children}; - }, - }} - />, - ); - expect(container.querySelector('pre')).toBeInTheDocument(); - expect(container.textContent).toContain('plain text'); - }); -}); diff --git a/packages/x-markdown/src/plugins/HighlightCode/style/index.ts b/packages/x-markdown/src/plugins/HighlightCode/style/index.ts deleted file mode 100644 index 48a9db221..000000000 --- a/packages/x-markdown/src/plugins/HighlightCode/style/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { CSSObject } from '@ant-design/cssinjs'; -import { mergeToken } from '@ant-design/cssinjs-utils'; -import { genStyleHooks } from '../../theme/genStyleUtils'; -import type { GenerateStyle } from '../../theme/interface'; -import type { FullToken, GetDefaultToken } from '../../theme/useToken'; - -export interface HighlightCodeToken extends FullToken<'HighlightCode'> {} - -const genHighlightCodeStyle: GenerateStyle = ( - token: HighlightCodeToken, -): CSSObject => { - const { componentCls } = token; - - return { - [componentCls]: { - [`${componentCls}-header`]: { - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - color: token.colorTextTitle, - background: token.colorBgTitle, - padding: token.paddingSM, - borderTopLeftRadius: token.borderRadius, - borderTopRightRadius: token.borderRadius, - }, - [`${componentCls}-code`]: { - borderBottomRightRadius: token.borderRadius, - borderBottomLeftRadius: token.borderRadius, - borderTopLeftRadius: 0, - borderTopRightRadius: 0, - background: token.colorBgContainer, - border: `1px solid ${token.colorBgTitle}`, - borderTop: 'none', - overflow: 'hidden', - 'pre,code': { - whiteSpace: 'pre', - fontSize: token.fontSize, - fontFamily: token.fontFamilyCode, - lineHeight: 2, - borderRadius: 0, - border: 'none', - }, - "code[class*='language-'],pre[class*='language-']": { - background: 'none', - }, - }, - [`&${componentCls}-rtl`]: { - direction: 'rtl', - }, - }, - }; -}; - -export const prepareComponentToken: GetDefaultToken<'HighlightCode'> = (token) => { - return { - colorBgTitle: token.colorFillContent, - colorBorderCode: token.colorBorderSecondary, - colorTextTitle: token.colorText, - }; -}; - -export default genStyleHooks<'HighlightCode'>( - 'HighlightCode', - (token) => { - const highlightCodeToken = mergeToken(token, {}); - return [genHighlightCodeStyle(highlightCodeToken)]; - }, - prepareComponentToken, -); diff --git a/packages/x-markdown/src/plugins/Latex/__test__/__snapshots__/index.test.tsx.snap b/packages/x-markdown/src/plugins/Latex/__test__/__snapshots__/index.test.tsx.snap index cadeda2cf..5e9593766 100644 --- a/packages/x-markdown/src/plugins/Latex/__test__/__snapshots__/index.test.tsx.snap +++ b/packages/x-markdown/src/plugins/Latex/__test__/__snapshots__/index.test.tsx.snap @@ -3,8 +3,7 @@ exports[`LaTeX Plugin should handle LaTeX with surrounding text 1`] = `

      This is an equation: @@ -117,8 +116,7 @@ exports[`LaTeX Plugin should handle LaTeX with surrounding text 1`] = ` exports[`LaTeX Plugin should handle align* syntax replacement 1`] = `

      Just plain text @@ -748,8 +744,7 @@ exports[`LaTeX Plugin should handle empty content 1`] = `

      `; exports[`LaTeX Plugin should handle mixed LaTeX syntaxes 1`] = `

      Inline: @@ -966,8 +961,7 @@ exports[`LaTeX Plugin should handle mixed LaTeX syntaxes 1`] = ` exports[`LaTeX Plugin should handle multiple LaTeX formulas 1`] = `

      latex: @@ -1174,8 +1167,7 @@ exports[`LaTeX Plugin should not throw error by default 1`] = ` exports[`LaTeX Plugin should render block LaTeX with $$..$$ syntax 1`] = `

      latex: @@ -1681,8 +1671,7 @@ exports[`LaTeX Plugin should render inline LaTeX with $$\\n..\\n$$ syntax 1`] = exports[`LaTeX Plugin should render inline LaTeX with $..$ syntax 1`] = `

      latex: diff --git a/packages/x-markdown/src/plugins/theme/genStyleUtils.ts b/packages/x-markdown/src/plugins/theme/genStyleUtils.ts deleted file mode 100644 index eae95bf7d..000000000 --- a/packages/x-markdown/src/plugins/theme/genStyleUtils.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { genStyleUtils } from '@ant-design/cssinjs-utils'; -import useXProviderContext from '@ant-design/x/es/x-provider/hooks/use-x-provider-context'; -import type { AliasToken, SeedToken } from 'antd/es/theme/internal'; -import { ComponentTokenMap } from './interface/components'; -import { useInternalToken } from './useToken'; - -export const { genStyleHooks, genComponentStyleHook, genSubStyleComponent } = genStyleUtils< - ComponentTokenMap, - AliasToken, - SeedToken ->({ - usePrefix: () => { - const { getPrefixCls, iconPrefixCls } = useXProviderContext(); - return { - iconPrefixCls, - rootPrefixCls: getPrefixCls(), - }; - }, - useToken: () => { - const [theme, realToken, hashId, token, cssVar] = useInternalToken(); - return { theme, realToken, hashId, token, cssVar }; - }, - useCSP: () => { - const { csp } = useXProviderContext(); - return csp ?? {}; - }, - layer: { - name: 'antdx', - dependencies: ['antd'], - }, -}); diff --git a/packages/x-markdown/src/plugins/theme/interface/alias.ts b/packages/x-markdown/src/plugins/theme/interface/alias.ts deleted file mode 100644 index 180a09d89..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/alias.ts +++ /dev/null @@ -1,612 +0,0 @@ -import type * as React from 'react'; - -import type { MapToken } from './maps'; - -// ====================================================================== -// == Alias Token == -// ====================================================================== -// 🔥🔥🔥🔥🔥🔥🔥 DO NOT MODIFY THIS. PLEASE CONTACT DESIGNER. 🔥🔥🔥🔥🔥🔥🔥 - -export interface AliasToken extends MapToken { - // Background - /** - * @nameZH 内容区域背景色(悬停) - * @nameEN Background color of content area (hover) - * @desc 控制内容区域背景色在鼠标悬停时的样式。 - * @descEN Control the style of background color of content area when mouse hovers over it. - */ - colorFillContentHover: string; - /** - * @nameZH 替代背景色 - * @nameEN Alternative background color - * @desc 控制元素替代背景色。 - * @descEN Control the alternative background color of element. - */ - colorFillAlter: string; - /** - * @nameZH 内容区域背景色 - * @nameEN Background color of content area - * @desc 控制内容区域的背景色。 - * @descEN Control the background color of content area. - */ - colorFillContent: string; - /** - * @nameZH 容器禁用态下的背景色 - * @nameEN Disabled container background color - * @desc 控制容器在禁用状态下的背景色。 - * @descEN Control the background color of container in disabled state. - */ - colorBgContainerDisabled: string; - /** - * @nameZH 文本悬停态背景色 - * @nameEN Text hover background color - * @desc 控制文本在悬停状态下的背景色。 - * @descEN Control the background color of text in hover state. - */ - colorBgTextHover: string; - /** - * @nameZH 文本激活态背景色 - * @nameEN Text active background color - * @desc 控制文本在激活状态下的背景色。 - * @descEN Control the background color of text in active state. - */ - colorBgTextActive: string; - - // Border - /** - * @nameZH 背景边框颜色 - * @nameEN Background border color - * @desc 控制元素背景边框的颜色。 - * @descEN Control the color of background border of element. - */ - colorBorderBg: string; - /** - * @nameZH 分割线颜色 - * @nameEN Separator Color - * @desc 用于作为分割线的颜色,此颜色和 colorBorderSecondary 的颜色一致,但是用的是透明色。 - * @descEN Used as the color of separator, this color is the same as colorBorderSecondary but with transparency. - */ - colorSplit: string; - - // Text - /** - * @nameZH 占位文本颜色 - * @nameEN Placeholder Text Color - * @desc 控制占位文本的颜色。 - * @descEN Control the color of placeholder text. - */ - colorTextPlaceholder: string; - /** - * @nameZH 禁用字体颜色 - * @nameEN Disabled Text Color - * @desc 控制禁用状态下的字体颜色。 - * @descEN Control the color of text in disabled state. - */ - colorTextDisabled: string; - /** - * @nameZH 标题字体颜色 - * @nameEN Heading Text Color - * @desc 控制标题字体颜色。 - * @descEN Control the font color of heading. - */ - colorTextHeading: string; - /** - * @nameZH 文本标签字体颜色 - * @nameEN Text label font color - * @desc 控制文本标签字体颜色。 - * @descEN Control the font color of text label. - */ - colorTextLabel: string; - /** - * @nameZH 文本描述字体颜色 - * @nameEN Text description font color - * @desc 控制文本描述字体颜色。 - * @descEN Control the font color of text description. - */ - colorTextDescription: string; - /** - * @nameZH 固定文本高亮颜色 - * @nameEN Fixed text highlight color - * @desc 控制带背景色的文本,例如 Primary Button 组件中的文本高亮颜色。 - * @descEN Control the highlight color of text with background color, such as the text in Primary Button components. - */ - colorTextLightSolid: string; - - /** - /** - * @nameZH 弱操作图标颜色 - * @nameEN Weak action icon color - * @desc 控制弱操作图标的颜色,例如 allowClear 或 Alert 关闭按钮。 - * @descEN Weak action. Such as `allowClear` or Alert close button - */ - colorIcon: string; - - /** */ - /** - * @nameZH 弱操作图标悬浮态颜色 - * @nameEN Weak action icon hover color - * @desc 控制弱操作图标在悬浮状态下的颜色,例如 allowClear 或 Alert 关闭按钮。 - * @descEN Weak action hover color. Such as `allowClear` or Alert close button - */ - colorIconHover: string; - - /** - * @nameZH 高亮颜色 - * @nameEN Highlight color - * @desc 控制页面元素高亮时的颜色。 - * @descEN Control the color of page element when highlighted. - */ - colorHighlight: string; - - /** - * @nameZH 输入组件的 Outline 颜色 - * @nameEN Input component outline color - * @desc 控制输入组件的外轮廓线颜色。 - * @descEN Control the outline color of input component. - */ - controlOutline: string; - - /** - * @nameZH 警告状态下的 Outline 颜色 - * @nameEN Warning outline color - * @desc 控制输入组件警告状态下的外轮廓线颜色。 - * @descEN Control the outline color of input component in warning state. - */ - colorWarningOutline: string; - - /** - * @nameZH 错误状态下的 Outline 颜色 - * @nameEN Error outline color - * @desc 控制输入组件错误状态下的外轮廓线颜色。 - * @descEN Control the outline color of input component in error state. - */ - colorErrorOutline: string; - - // Font - /** - * @nameZH 选择器、级联选择器等中的操作图标字体大小 - * @nameEN Operation icon font size in Select, Cascader, etc. - * @desc 控制选择器、级联选择器等中的操作图标字体大小。正常情况下与 fontSizeSM 相同。 - * @descEN Control the font size of operation icon in Select, Cascader, etc. Normally same as fontSizeSM. - */ - fontSizeIcon: number; - - /** - * @nameZH 标题类组件(如 h1、h2、h3)或选中项的字体粗细 - * @nameEN Font weight for heading components (such as h1, h2, h3) or selected item - * @desc 控制标题类组件(如 h1、h2、h3)或选中项的字体粗细。 - * @descEN Control the font weight of heading components (such as h1, h2, h3) or selected item. - */ - fontWeightStrong: number; - - // Control - - /** - * @nameZH 输入组件的外轮廓线宽度 - * @nameEN Input component outline width - * @desc 控制输入组件的外轮廓线宽度。 - * @descEN Control the outline width of input component. - */ - controlOutlineWidth: number; - - /** - * @nameZH 控制组件项在鼠标悬浮时的背景颜色 - * @nameEN Background color of control component item when hovering - * @desc 控制组件项在鼠标悬浮时的背景颜色。 - * @descEN Control the background color of control component item when hovering. - */ - controlItemBgHover: string; // Note. It also is a color - - /** - * @nameZH 控制组件项在激活状态下的背景颜色 - * @nameEN Background color of control component item when active - * @desc 控制组件项在激活状态下的背景颜色。 - * @descEN Control the background color of control component item when active. - */ - controlItemBgActive: string; // Note. It also is a color - - /** - * @nameZH 控制组件项在鼠标悬浮且激活状态下的背景颜色 - * @nameEN Background color of control component item when hovering and active - * @desc 控制组件项在鼠标悬浮且激活状态下的背景颜色。 - * @descEN Control the background color of control component item when hovering and active. - */ - controlItemBgActiveHover: string; // Note. It also is a color - - /** - * @nameZH 控制组件的交互大小 - * @nameEN Interactive size of control component - * @desc 控制组件的交互大小。 - * @descEN Control the interactive size of control component. - */ - controlInteractiveSize: number; - - /** - * @nameZH 控制组件项在禁用状态下的激活背景颜色 - * @nameEN Background color of control component item when active and disabled - * @desc 控制组件项在禁用状态下的激活背景颜色。 - * @descEN Control the background color of control component item when active and disabled. - */ - controlItemBgActiveDisabled: string; // Note. It also is a color - - // Line - /** - * @nameZH 线条宽度(聚焦态) - * @nameEN Line width(focus state) - * @desc 控制线条的宽度,当组件处于聚焦态时。 - * @descEN Control the width of the line when the component is in focus state. - */ - lineWidthFocus: number; - - // Padding - /** - * @nameZH 极小内间距 - * @nameEN Extra extra small padding - * @desc 控制元素的极小内间距。 - * @descEN Control the extra extra small padding of the element. - */ - paddingXXS: number; - /** - * @nameZH 特小内间距 - * @nameEN Extra small padding - * @desc 控制元素的特小内间距。 - * @descEN Control the extra small padding of the element. - */ - paddingXS: number; - /** - * @nameZH 小内间距 - * @nameEN Small padding - * @desc 控制元素的小内间距。 - * @descEN Control the small padding of the element. - */ - paddingSM: number; - /** - * @nameZH 内间距 - * @nameEN Padding - * @desc 控制元素的内间距。 - * @descEN Control the padding of the element. - */ - padding: number; - /** - * @nameZH 中等内间距 - * @nameEN Medium padding - * @desc 控制元素的中等内间距。 - * @descEN Control the medium padding of the element. - */ - paddingMD: number; - /** - * @nameZH 大内间距 - * @nameEN Large padding - * @desc 控制元素的大内间距。 - * @descEN Control the large padding of the element. - */ - paddingLG: number; - /** - * @nameZH 特大内间距 - * @nameEN Extra large padding - * @desc 控制元素的特大内间距。 - * @descEN Control the extra large padding of the element. - */ - paddingXL: number; - - // Padding Content - /** - * @nameZH 内容水平内间距(LG) - * @nameEN Content horizontal padding (LG) - * @desc 控制内容元素水平内间距,适用于大屏幕设备。 - * @descEN Control the horizontal padding of content element, suitable for large screen devices. - */ - paddingContentHorizontalLG: number; - /** - * @nameZH 内容水平内间距 - * @nameEN Content horizontal padding - * @desc 控制内容元素水平内间距。 - * @descEN Control the horizontal padding of content element. - */ - paddingContentHorizontal: number; - /** - * @nameZH 内容水平内间距(SM) - * @nameEN Content horizontal padding (SM) - * @desc 控制内容元素水平内间距,适用于小屏幕设备。 - * @descEN Control the horizontal padding of content element, suitable for small screen devices. - */ - paddingContentHorizontalSM: number; - /** - * @nameZH 内容垂直内间距(LG) - * @nameEN Content vertical padding (LG) - * @desc 控制内容元素垂直内间距,适用于大屏幕设备。 - * @descEN Control the vertical padding of content element, suitable for large screen devices. - */ - paddingContentVerticalLG: number; - /** - * @nameZH 内容垂直内间距 - * @nameEN Content vertical padding - * @desc 控制内容元素垂直内间距。 - * @descEN Control the vertical padding of content element. - */ - paddingContentVertical: number; - /** - * @nameZH 内容垂直内间距(SM) - * @nameEN Content vertical padding (SM) - * @desc 控制内容元素垂直内间距,适用于小屏幕设备。 - * @descEN Control the vertical padding of content element, suitable for small screen devices. - */ - paddingContentVerticalSM: number; - - // Margin - /** - * @nameZH 外边距 XXS - * @nameEN Margin XXS - * @desc 控制元素外边距,最小尺寸。 - * @descEN Control the margin of an element, with the smallest size. - */ - marginXXS: number; - /** - * @nameZH 外边距 XS - * @nameEN Margin XS - * @desc 控制元素外边距,小尺寸。 - * @descEN Control the margin of an element, with a small size. - */ - marginXS: number; - /** - * @nameZH 外边距 SM - * @nameEN Margin SM - * @desc 控制元素外边距,中小尺寸。 - * @descEN Control the margin of an element, with a medium-small size. - */ - marginSM: number; - /** - * @nameZH 外边距 - * @nameEN Margin - * @desc 控制元素外边距,中等尺寸。 - * @descEN Control the margin of an element, with a medium size. - */ - margin: number; - /** - * @nameZH 外边距 MD - * @nameEN Margin MD - * @desc 控制元素外边距,中大尺寸。 - * @descEN Control the margin of an element, with a medium-large size. - */ - marginMD: number; - /** - * @nameZH 外边距 LG - * @nameEN Margin LG - * @desc 控制元素外边距,大尺寸。 - * @descEN Control the margin of an element, with a large size. - */ - marginLG: number; - /** - * @nameZH 外边距 XL - * @nameEN Margin XL - * @desc 控制元素外边距,超大尺寸。 - * @descEN Control the margin of an element, with an extra-large size. - */ - marginXL: number; - /** - * @nameZH 外边距 XXL - * @nameEN Margin XXL - * @desc 控制元素外边距,最大尺寸。 - * @descEN Control the margin of an element, with the largest size. - */ - marginXXL: number; - - // =============== Legacy: should be remove =============== - /** - * @nameZH 加载状态透明度 - * @nameEN Loading opacity - * @desc 控制加载状态的透明度。 - * @descEN Control the opacity of the loading state. - */ - opacityLoading: number; - - /** - * @nameZH 一级阴影 - * @nameEN Box shadow - * @desc 控制元素阴影样式。 - * @descEN Control the box shadow style of an element. - */ - boxShadow: string; - /** - * @nameZH 二级阴影 - * @nameEN Secondary box shadow - * @desc 控制元素二级阴影样式。 - * @descEN Control the secondary box shadow style of an element. - */ - boxShadowSecondary: string; - /** - * @nameZH 三级阴影 - * @nameEN Tertiary box shadow - * @desc 控制元素三级盒子阴影样式。 - * @descEN Control the tertiary box shadow style of an element. - */ - boxShadowTertiary: string; - - /** - * @nameZH 链接文本装饰 - * @nameEN Link text decoration - * @desc 控制链接文本的装饰样式。 - * @descEN Control the text decoration style of a link. - */ - linkDecoration: React.CSSProperties['textDecoration']; - /** - * @nameZH 链接鼠标悬浮时文本装饰 - * @nameEN Link text decoration on mouse hover - * @desc 控制链接鼠标悬浮时文本的装饰样式。 - * @descEN Control the text decoration style of a link on mouse hover. - */ - linkHoverDecoration: React.CSSProperties['textDecoration']; - /** - * @nameZH 链接聚焦时文本装饰 - * @nameEN Link text decoration on focus - * @desc 控制链接聚焦时文本的装饰样式。 - * @descEN Control the text decoration style of a link on focus. - */ - linkFocusDecoration: React.CSSProperties['textDecoration']; - - /** - * @nameZH 控制水平内间距 - * @nameEN Control horizontal padding - * @desc 控制元素水平内间距。 - * @descEN Control the horizontal padding of an element. - */ - controlPaddingHorizontal: number; - /** - * @nameZH 控制中小尺寸水平内间距 - * @nameEN Control horizontal padding with a small-medium size - * @desc 控制元素中小尺寸水平内间距。 - * @descEN Control the horizontal padding of an element with a small-medium size. - */ - controlPaddingHorizontalSM: number; - - // Media queries breakpoints - /** - * @nameZH 屏幕宽度(像素) - 超小屏幕 - * @nameEN Screen width (pixels) - Extra small screens - * @desc 控制超小屏幕的屏幕宽度。 - * @descEN Control the screen width of extra small screens. - */ - screenXS: number; - /** - * @nameZH 屏幕宽度(像素) - 超小屏幕最小值 - * @nameEN Screen width (pixels) - Extra small screens minimum value - * @desc 控制超小屏幕的最小宽度。 - * @descEN Control the minimum width of extra small screens. - */ - screenXSMin: number; - /** - * @nameZH 屏幕宽度(像素) - 超小屏幕最大值 - * @nameEN Screen width (pixels) - Extra small screens maximum value - * @desc 控制超小屏幕的最大宽度。 - * @descEN Control the maximum width of extra small screens. - */ - screenXSMax: number; - /** - * @nameZH 屏幕宽度(像素) - 小屏幕 - * @nameEN Screen width (pixels) - Small screens - * @desc 控制小屏幕的屏幕宽度。 - * @descEN Control the screen width of small screens. - */ - screenSM: number; - /** - * @nameZH 屏幕宽度(像素) - 小屏幕最小值 - * @nameEN Screen width (pixels) - Small screens minimum value - * @desc 控制小屏幕的最小宽度。 - * @descEN Control the minimum width of small screens. - */ - screenSMMin: number; - /** - * @nameZH 屏幕宽度(像素) - 小屏幕最大值 - * @nameEN Screen width (pixels) - Small screens maximum value - * @desc 控制小屏幕的最大宽度。 - * @descEN Control the maximum width of small screens. - */ - screenSMMax: number; - /** - * @nameZH 屏幕宽度(像素) - 中等屏幕 - * @nameEN Screen width (pixels) - Medium screens - * @desc 控制中等屏幕的屏幕宽度。 - * @descEN Control the screen width of medium screens. - */ - screenMD: number; - /** - * @nameZH 屏幕宽度(像素) - 中等屏幕最小值 - * @nameEN Screen width (pixels) - Medium screens minimum value - * @desc 控制中等屏幕的最小宽度。 - * @descEN Control the minimum width of medium screens. - */ - screenMDMin: number; - /** - * @nameZH 屏幕宽度(像素) - 中等屏幕最大值 - * @nameEN Screen width (pixels) - Medium screens maximum value - * @desc 控制中等屏幕的最大宽度。 - * @descEN Control the maximum width of medium screens. - */ - screenMDMax: number; - /** - * @nameZH 屏幕宽度(像素) - 大屏幕 - * @nameEN Screen width (pixels) - Large screens - * @desc 控制大屏幕的屏幕宽度。 - * @descEN Control the screen width of large screens. - */ - screenLG: number; - /** - * @nameZH 屏幕宽度(像素) - 大屏幕最小值 - * @nameEN Screen width (pixels) - Large screens minimum value - * @desc 控制大屏幕的最小宽度。 - * @descEN Control the minimum width of large screens. - */ - screenLGMin: number; - /** - * @nameZH 屏幕宽度(像素) - 大屏幕最大值 - * @nameEN Screen width (pixels) - Large screens maximum value - * @desc 控制大屏幕的最大宽度。 - * @descEN Control the maximum width of large screens. - */ - screenLGMax: number; - /** - * @nameZH 屏幕宽度(像素) - 超大屏幕 - * @nameEN Screen width (pixels) - Extra large screens - * @desc 控制超大屏幕的屏幕宽度。 - * @descEN Control the screen width of extra large screens. - */ - screenXL: number; - /** - * @nameZH 屏幕宽度(像素) - 超大屏幕最小值 - * @nameEN Screen width (pixels) - Extra large screens minimum value - * @desc 控制超大屏幕的最小宽度。 - * @descEN Control the minimum width of extra large screens. - */ - screenXLMin: number; - /** - * @nameZH 屏幕宽度(像素) - 超大屏幕最大值 - * @nameEN Screen width (pixels) - Extra large screens maximum value - * @desc 控制超大屏幕的最大宽度。 - * @descEN Control the maximum width of extra large screens. - */ - screenXLMax: number; - /** - * @nameZH 屏幕宽度(像素) - 超超大屏幕 - * @nameEN Screen width (pixels) - Extra extra large screens - * @desc 控制超超大屏幕的屏幕宽度。 - * @descEN Control the screen width of extra extra large screens. - */ - screenXXL: number; - /** - * @nameZH 屏幕宽度(像素) - 超超大屏幕最小值 - * @nameEN Screen width (pixels) - Extra extra large screens minimum value - * @desc 控制超超大屏幕的最小宽度。 - * @descEN Control the minimum width of extra extra large screens. - */ - screenXXLMin: number; - - /** - * @deprecated - * Used for DefaultButton, Switch which has default outline - * @desc 默认样式的 Outline 颜色 - * @descEN Default style outline color. - */ - controlTmpOutline: string; - - // FIXME: component box-shadow, should be removed - /** @internal */ - boxShadowPopoverArrow: string; - /** @internal */ - boxShadowCard: string; - /** @internal */ - boxShadowDrawerRight: string; - /** @internal */ - boxShadowDrawerLeft: string; - /** @internal */ - boxShadowDrawerUp: string; - /** @internal */ - boxShadowDrawerDown: string; - /** @internal */ - boxShadowTabsOverflowLeft: string; - /** @internal */ - boxShadowTabsOverflowRight: string; - /** @internal */ - boxShadowTabsOverflowTop: string; - /** @internal */ - boxShadowTabsOverflowBottom: string; -} diff --git a/packages/x-markdown/src/plugins/theme/interface/components.ts b/packages/x-markdown/src/plugins/theme/interface/components.ts deleted file mode 100644 index e01f47936..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/components.ts +++ /dev/null @@ -1,48 +0,0 @@ -export interface HighlightCodePluginToken { - /** - * @desc 标题背景颜色 - * @descEN Title background color - */ - colorBgTitle: string; - /** - * @desc 标题文本颜色 - * @descEN Title text color - */ - colorTextTitle: string; - /** - * @desc 代码块边框颜色 - * @descEN Code block border color - */ - colorBorderCode: string; -} - -export interface MermaidPluginToken { - /** - * @desc 标题背景颜色 - * @descEN Title background color - */ - colorBgTitle: string; - - /** - * @desc 标题文本颜色 - * @descEN Title text color - */ - colorTextTitle: string; - - /** - * @desc 代码块边框颜色 - * @descEN Code block border color - */ - colorBorderCode: string; - - /** - * @desc 图表边框颜色 - * @descEN Graph border color - */ - colorBorderGraph: string; -} - -export interface ComponentTokenMap { - Mermaid: MermaidPluginToken; - HighlightCode: HighlightCodePluginToken; -} diff --git a/packages/x-markdown/src/plugins/theme/interface/cssinjs-utils.ts b/packages/x-markdown/src/plugins/theme/interface/cssinjs-utils.ts deleted file mode 100644 index f71a50694..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/cssinjs-utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { - FullToken as FullTokenTypeUtil, - GenStyleFn as GenStyleFnTypeUtil, - GetDefaultToken as GetDefaultTokenTypeUtil, - GlobalToken as GlobalTokenTypeUtil, - OverrideTokenMap as OverrideTokenTypeUtil, - TokenMapKey, -} from '@ant-design/cssinjs-utils'; - -import type { AliasToken } from './alias'; -import type { ComponentTokenMap } from './components'; - -/** Final token which contains the components level override */ -export type GlobalToken = GlobalTokenTypeUtil; - -export type OverrideToken = OverrideTokenTypeUtil; - -export type OverrideComponent = TokenMapKey; - -export type FullToken> = FullTokenTypeUtil< - ComponentTokenMap, - AliasToken, - C ->; - -export type GetDefaultToken> = GetDefaultTokenTypeUtil< - ComponentTokenMap, - AliasToken, - C ->; - -export type GenStyleFn> = GenStyleFnTypeUtil< - ComponentTokenMap, - AliasToken, - C ->; diff --git a/packages/x-markdown/src/plugins/theme/interface/index.ts b/packages/x-markdown/src/plugins/theme/interface/index.ts deleted file mode 100644 index d92169275..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/index.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { CSSInterpolation, DerivativeFunc } from '@ant-design/cssinjs'; - -import type { AliasToken } from './alias'; -import type { MapToken } from './maps'; -import type { SeedToken } from './seeds'; - -export type AnyObject = Record; - -export type MappingAlgorithm = DerivativeFunc; - -export type { AliasToken } from './alias'; -export type { ComponentTokenMap } from './components'; -export type { - FullToken, - GenStyleFn, - GetDefaultToken, - GlobalToken, - OverrideComponent, - OverrideToken, -} from './cssinjs-utils'; -export type { - ColorMapToken, - ColorNeutralMapToken, - CommonMapToken, - FontMapToken, - HeightMapToken, - MapToken, - SizeMapToken, - StyleMapToken, -} from './maps'; -export type { - ColorPalettes, - LegacyColorPalettes, - PresetColorKey, - PresetColorType, -} from './presetColors'; -export { PresetColors } from './presetColors'; -export type { SeedToken } from './seeds'; - -export type UseComponentStyleResult = [(node: React.ReactNode) => React.ReactElement, string]; - -export type GenerateStyle< - ComponentToken extends AnyObject = AliasToken, - ReturnType = CSSInterpolation, -> = (token: ComponentToken) => ReturnType; diff --git a/packages/x-markdown/src/plugins/theme/interface/maps/colors.ts b/packages/x-markdown/src/plugins/theme/interface/maps/colors.ts deleted file mode 100644 index fe3d1b148..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/maps/colors.ts +++ /dev/null @@ -1,639 +0,0 @@ -export interface ColorNeutralMapToken { - /** - * @internal - */ - colorTextBase: string; - - /** - * @internal - */ - colorBgBase: string; - - // ---------- Text ---------- // - - /** - * @nameZH 一级文本色 - * @nameEN Text Color - * @desc 最深的文本色。为了符合W3C标准,默认的文本颜色使用了该色,同时这个颜色也是最深的中性色。 - * @descEN Default text color which comply with W3C standards, and this color is also the darkest neutral color. - */ - colorText: string; - - /** - * @nameZH 二级文本色 - * @nameEN Secondary Text Color - * @desc 作为第二梯度的文本色,一般用在不那么需要强化文本颜色的场景,例如 Label 文本、Menu 的文本选中态等场景。 - * @descEN The second level of text color is generally used in scenarios where text color is not emphasized, such as label text, menu text selection state, etc. - */ - colorTextSecondary: string; - - /** - * @nameZH 三级文本色 - * @desc 第三级文本色一般用于描述性文本,例如表单的中的补充说明文本、列表的描述性文本等场景。 - * @descEN The third level of text color is generally used for descriptive text, such as form supplementary explanation text, list descriptive text, etc. - */ - colorTextTertiary: string; - - /** - * @nameZH 四级文本色 - * @desc 第四级文本色是最浅的文本色,例如表单的输入提示文本、禁用色文本等。 - * @descEN The fourth level of text color is the lightest text color, such as form input prompt text, disabled color text, etc. - */ - colorTextQuaternary: string; - - // ---------- Border ---------- // - - /** - * @nameZH 一级边框色 - * @nameEN Default Border Color - * @desc 默认使用的边框颜色, 用于分割不同的元素,例如:表单的分割线、卡片的分割线等。 - * @descEN Default border color, used to separate different elements, such as: form separator, card separator, etc. - */ - colorBorder: string; - - /** - * @nameZH 二级边框色 - * @nameEN Secondary Border Color - * @desc 比默认使用的边框色要浅一级,此颜色和 colorSplit 的颜色一致。使用的是实色。 - * @descEN Slightly lighter than the default border color, this color is the same as `colorSplit`. Solid color is used. - */ - colorBorderSecondary: string; - - /** - * @nameZH 禁用态边框颜色 - * @nameEN Disabled state border color - * @desc 控制元素在禁用状态下的边框颜色。 - * @descEN Control the border color of the element in the disabled state. - */ - colorBorderDisabled: string; - - // ---------- Fill ---------- // - - /** - * @nameZH 一级填充色 - * @desc 最深的填充色,用于拉开与二、三级填充色的区分度,目前只用在 Slider 的 hover 效果。 - * @descEN The darkest fill color is used to distinguish between the second and third level of fill color, and is currently only used in the hover effect of Slider. - */ - colorFill: string; - - /** - * @nameZH 二级填充色 - * @desc 二级填充色可以较为明显地勾勒出元素形体,如 Rate、Skeleton 等。也可以作为三级填充色的 Hover 状态,如 Table 等。 - * @descEN The second level of fill color can outline the shape of the element more clearly, such as Rate, Skeleton, etc. It can also be used as the Hover state of the third level of fill color, such as Table, etc. - */ - colorFillSecondary: string; - - /** - * @nameZH 三级填充色 - * @desc 三级填充色用于勾勒出元素形体的场景,如 Slider、Segmented 等。如无强调需求的情况下,建议使用三级填色作为默认填色。 - * @descEN The third level of fill color is used to outline the shape of the element, such as Slider, Segmented, etc. If there is no emphasis requirement, it is recommended to use the third level of fill color as the default fill color. - */ - colorFillTertiary: string; - - /** - * @nameZH 四级填充色 - * @desc 最弱一级的填充色,适用于不易引起注意的色块,例如斑马纹、区分边界的色块等。 - * @descEN The weakest level of fill color is suitable for color blocks that are not easy to attract attention, such as zebra stripes, color blocks that distinguish boundaries, etc. - */ - colorFillQuaternary: string; - - // ---------- Surface ---------- // - - /** - * @nameZH 布局背景色 - * @nameEN Layout Background Color - * @desc 该色用于页面整体布局的背景色,只有需要在页面中处于 B1 的视觉层级时才会使用该 token,其他用法都是错误的 - * @descEN This color is used for the background color of the overall layout of the page. This token will only be used when it is necessary to be at the B1 visual level in the page. Other usages are wrong. - */ - colorBgLayout: string; - - /** - * @nameZH 组件容器背景色 - * @desc 组件的容器背景色,例如:默认按钮、输入框等。务必不要将其与 `colorBgElevated` 混淆。 - * @descEN Container background color, e.g: default button, input box, etc. Be sure not to confuse this with `colorBgElevated`. - */ - colorBgContainer: string; - - /** - * @nameZH 浮层容器背景色 - * @desc 浮层容器背景色,在暗色模式下该 token 的色值会比 `colorBgContainer` 要亮一些。例如:模态框、弹出框、菜单等。 - * @descEN Container background color of the popup layer, in dark mode the color value of this token will be a little brighter than `colorBgContainer`. E.g: modal, pop-up, menu, etc. - */ - colorBgElevated: string; - - /** - * @nameZH 引起注意的背景色 - * @desc 该色用于引起用户强烈关注注意的背景色,目前只用在 Tooltip 的背景色上。 - * @descEN This color is used to draw the user's strong attention to the background color, and is currently only used in the background color of Tooltip. - */ - colorBgSpotlight: string; - /** - * @nameZH 毛玻璃容器背景色 - * @nameEN Frosted glass container background color - * @desc 控制毛玻璃容器的背景色,通常为透明色。 - * @descEN Control the background color of frosted glass container, usually transparent. - */ - colorBgBlur: string; - - // ---------- Solid ---------- // - - /** - * @desc 实心的背景颜色,目前只用在默认实心按钮背景色上。 - * @descEN Solid background color, currently only used for the default solid button background color. - */ - colorBgSolid: string; - /** - * @desc 实心的背景颜色激活态,目前只用在默认实心按钮的 active 效果。 - * @descEN Solid background color active state, currently only used in the active effect of the default solid button. - */ - colorBgSolidActive: string; - /** - * @desc 实心的背景颜色悬浮态,目前只用在默认实心按钮的 hover 效果。 - * @descEN Solid background color hover state, currently only used in the hover effect of the default solid button. - */ - colorBgSolidHover: string; -} - -/** - * 品牌色梯度变量 - */ -interface ColorPrimaryMapToken { - /** - * @nameZH 品牌主色 - * @nameEN Primary color of the brand - * @desc 品牌色是体现产品特性和传播理念最直观的视觉元素之一,用于产品的主色调、主按钮、主图标、主文本等 - * @descEN The brand color is one of the most intuitive visual elements that reflects product characteristics and communication concepts, and is used for the main color tone, main buttons, main icons, main text, etc. of the product. - */ - colorPrimary: string; // 6 - - /** - * @nameZH 主色浅色背景色 - * @nameEN Light background color of primary color - * @desc 主色浅色背景颜色,一般用于视觉层级较弱的选中状态。 - * @descEN Light background color of primary color, usually used for weak visual level selection state. - */ - colorPrimaryBg: string; // 1 - - /** - * @nameZH 主色浅色背景悬浮态 - * @nameEN Hover state of light background color of primary color - * @desc 与主色浅色背景颜色相对应的悬浮态颜色。 - * @descEN The hover state color corresponding to the light background color of the primary color. - */ - colorPrimaryBgHover: string; // 2 - - /** - * @nameZH 主色描边色 - * @nameEN Border color of primary color - * @desc 主色梯度下的描边用色,用在 Slider 等组件的描边上。 - * @descEN The stroke color under the main color gradient, used on the stroke of components such as Slider. - */ - colorPrimaryBorder: string; // 3 - - /** - * @nameZH 主色描边色悬浮态 - * @nameEN Hover state of border color of primary color - * @desc 主色梯度下的描边用色的悬浮态,Slider 、Button 等组件的描边 Hover 时会使用。 - * @descEN The hover state of the stroke color under the main color gradient, which will be used when the stroke Hover of components such as Slider and Button. - */ - colorPrimaryBorderHover: string; // 4 - - /** - * @nameZH 主色悬浮态 - * @nameEN Hover state of primary color - * @desc 主色梯度下的悬浮态。 - * @descEN Hover state under the main color gradient. - */ - colorPrimaryHover: string; // 5 - - /** - * @nameZH 主色激活态 - * @nameEN Active state of primary color - * @desc 主色梯度下的深色激活态。 - * @descEN Dark active state under the main color gradient. - */ - colorPrimaryActive: string; // 7 - - /** - * @nameZH 主色文本悬浮态 - * @nameEN Hover state of text color of primary color - * @desc 主色梯度下的文本悬浮态。 - * @descEN Hover state of text color under the main color gradient. - */ - colorPrimaryTextHover: string; // 8 - - /** - * @nameZH 主色文本 - * @nameEN Text color of primary color - * @desc 主色梯度下的文本颜色。 - * @descEN Text color under the main color gradient. - */ - colorPrimaryText: string; // 9 - - /** - * @nameZH 主色文本激活态 - * @nameEN Active state of text color of primary color - * @desc 主色梯度下的文本激活态。 - * @descEN Active state of text color under the main color gradient. - */ - colorPrimaryTextActive: string; // 10 -} - -interface ColorSuccessMapToken { - /** - * @nameZH 成功色的浅色背景颜色 - * @nameEN Light Background Color of Success Color - * @desc 成功色的浅色背景颜色,用于 Tag 和 Alert 的成功态背景色 - * @descEN Light background color of success color, used for Tag and Alert success state background color - */ - colorSuccessBg: string; // 1 - - /** - * @nameZH 成功色的浅色背景色悬浮态 - * @nameEN Hover State Color of Light Success Background - * @desc 成功色浅色背景颜色,一般用于视觉层级较弱的选中状态,不过 antd 目前没有使用到该 token - * @descEN Light background color of success color, but antd does not use this token currently - */ - colorSuccessBgHover: string; // 2 - - /** - * @nameZH 成功色的描边色 - * @nameEN Border Color of Success Color - * @desc 成功色的描边色,用于 Tag 和 Alert 的成功态描边色 - * @descEN Border color of success color, used for Tag and Alert success state border color - */ - colorSuccessBorder: string; // 3 - - /** - * @nameZH 成功色的描边色悬浮态 - * @nameEN Hover State Color of Success Border - * @desc 成功色的描边色悬浮态 - * @descEN Hover state color of success color border - */ - colorSuccessBorderHover: string; // 4 - - /** - * @nameZH 成功色的深色悬浮态 - * @nameEN Hover State Color of Dark Success - * @desc 成功色的深色悬浮态 - * @descEN Hover state color of dark success color - */ - colorSuccessHover: string; // 5 - - /** - * @nameZH 成功色 - * @nameEN Success Color - * @desc 默认的成功色,如 Result、Progress 等组件中都有使用该颜色 - * @descEN Default success color, used in components such as Result and Progress - */ - colorSuccess: string; // 6 - - /** - * @nameZH 成功色的深色激活态 - * @nameEN Active State Color of Dark Success - * @desc 成功色的深色激活态 - * @descEN Active state color of dark success color - */ - colorSuccessActive: string; // 7 - - /** - * @nameZH 成功色的文本悬浮态 - * @nameEN Hover State Color of Success Text - * @desc 成功色的文本悬浮态 - * @descEN Hover state color of success color text - */ - colorSuccessTextHover: string; // 8 - - /** - * @nameZH 成功色的文本默认态 - * @nameEN Default State Color of Success Text - * @desc 成功色的文本默认态 - * @descEN Default state color of success color text - */ - colorSuccessText: string; // 9 - - /** - * @nameZH 成功色的文本激活态 - * @nameEN Active State Color of Success Text - * @desc 成功色的文本激活态 - * @descEN Active state color of success color text - */ - colorSuccessTextActive: string; // 10 -} - -interface ColorWarningMapToken { - /** - * @nameZH 警戒色的浅色背景颜色 - * @nameEN Warning background color - * @desc 警戒色的浅色背景颜色 - * @descEN The background color of the warning state. - */ - colorWarningBg: string; // 1 - - /** - * @nameZH 警戒色的浅色背景色悬浮态 - * @nameEN Warning background color hover state - * @desc 警戒色的浅色背景色悬浮态 - * @descEN The hover state background color of the warning state. - */ - colorWarningBgHover: string; // 2 - - /** - * @nameZH 警戒色的描边色 - * @nameEN Warning border color - * @desc 警戒色的描边色 - * @descEN The border color of the warning state. - */ - colorWarningBorder: string; // 3 - - /** - * @nameZH 警戒色的描边色悬浮态 - * @nameEN Warning border color hover state - * @desc 警戒色的描边色悬浮态 - * @descEN The hover state border color of the warning state. - */ - colorWarningBorderHover: string; // 4 - - /** - * @nameZH 警戒色的深色悬浮态 - * @nameEN Warning hover color - * @desc 警戒色的深色悬浮态 - * @descEN The hover state of the warning color. - */ - colorWarningHover: string; // 5 - - /** - * @nameZH 警戒色 - * @nameEN Warning color - * @desc 最常用的警戒色,例如 Notification、 Alert等警告类组件或 Input 输入类等组件会使用该颜色 - * @descEN The most commonly used warning color, used for warning components such as Notification, Alert, or input components. - */ - colorWarning: string; // 6 - - /** - * @nameZH 警戒色的深色激活态 - * @nameEN Warning active color - * @desc 警戒色的深色激活态 - * @descEN The active state of the warning color. - */ - colorWarningActive: string; // 7 - - /** - * @nameZH 警戒色的文本悬浮态 - * @nameEN Warning text hover state - * @desc 警戒色的文本悬浮态 - * @descEN The hover state of the text in the warning color. - */ - colorWarningTextHover: string; // 8 - - /** - * @nameZH 警戒色的文本默认态 - * @nameEN Warning text default state - * @desc 警戒色的文本默认态 - * @descEN The default state of the text in the warning color. - */ - colorWarningText: string; // 9 - - /** - * @nameZH 警戒色的文本激活态 - * @nameEN Warning text active state - * @desc 警戒色的文本激活态 - * @descEN The active state of the text in the warning color. - */ - colorWarningTextActive: string; // 10 -} - -interface ColorInfoMapToken { - /** - * @nameZH 信息色的浅色背景颜色 - * @nameEN Light background color of information color - * @desc 信息色的浅色背景颜色。 - * @descEN Light background color of information color. - */ - colorInfoBg: string; // 1 - - /** - * @nameZH 信息色的浅色背景色悬浮态 - * @nameEN Hover state of light background color of information color - * @desc 信息色的浅色背景色悬浮态。 - * @descEN Hover state of light background color of information color. - */ - colorInfoBgHover: string; // 2 - - /** - * @nameZH 信息色的描边色 - * @nameEN Border color of information color - * @desc 信息色的描边色。 - * @descEN Border color of information color. - */ - colorInfoBorder: string; // 3 - - /** - * @nameZH 信息色的描边色悬浮态 - * @nameEN Hover state of border color of information color - * @desc 信息色的描边色悬浮态。 - * @descEN Hover state of border color of information color. - */ - colorInfoBorderHover: string; // 4 - - /** - * @nameZH 信息色的深色悬浮态 - * @nameEN Hover state of dark color of information color - * @desc 信息色的深色悬浮态。 - * @descEN Hover state of dark color of information color. - */ - colorInfoHover: string; // 5 - - /** - * @nameZH 信息色 - * @nameEN Information color - * @desc 信息色。 - * @descEN Information color. - */ - colorInfo: string; // 6 - - /** - * @nameZH 信息色的深色激活态 - * @nameEN Active state of dark color of information color - * @desc 信息色的深色激活态。 - * @descEN Active state of dark color of information color. - */ - colorInfoActive: string; // 7 - - /** - * @nameZH 信息色的文本悬浮态 - * @nameEN Hover state of text color of information color - * @desc 信息色的文本悬浮态。 - * @descEN Hover state of text color of information color. - */ - colorInfoTextHover: string; // 8 - - /** - * @nameZH 信息色的文本默认态 - * @nameEN Default state of text color of information color - * @desc 信息色的文本默认态。 - * @descEN Default state of text color of information color. - */ - colorInfoText: string; // 9 - - /** - * @nameZH 信息色的文本激活态 - * @nameEN Active state of text color of information color - * @desc 信息色的文本激活态。 - * @descEN Active state of text color of information color. - */ - colorInfoTextActive: string; // 10 -} - -interface ColorErrorMapToken { - /** - * @nameZH 错误色的浅色背景颜色 - * @nameEN Error background color - * @desc 错误色的浅色背景颜色 - * @descEN The background color of the error state. - */ - colorErrorBg: string; // 1 - - /** - * @nameZH 错误色的浅色背景色悬浮态 - * @nameEN Error background color hover state - * @desc 错误色的浅色背景色悬浮态 - * @descEN The hover state background color of the error state. - */ - colorErrorBgHover: string; // 2 - - /** - * @nameZH 错误色的浅色填充背景色悬浮态 - * @nameEN Wrong color fill background color suspension state - * @desc 错误色的浅色填充背景色悬浮态,目前只用在危险填充按钮的 hover 效果。 - * @descEN The wrong color fills the background color of the suspension state, which is currently only used in the hover effect of the dangerous filled button. - */ - colorErrorBgFilledHover: string; // 2.5 - - /** - * @nameZH 错误色的浅色背景色激活态 - * @nameEN Error background color active state - * @desc 错误色的浅色背景色激活态 - * @descEN The active state background color of the error state. - */ - colorErrorBgActive: string; // 3 - - /** - * @nameZH 错误色的描边色 - * @nameEN Error border color - * @desc 错误色的描边色 - * @descEN The border color of the error state. - */ - colorErrorBorder: string; // 3 - - /** - * @nameZH 错误色的描边色悬浮态 - * @nameEN Error border color hover state - * @desc 错误色的描边色悬浮态 - * @descEN The hover state border color of the error state. - */ - colorErrorBorderHover: string; // 4 - - /** - * @nameZH 错误色的深色悬浮态 - * @nameEN Error hover color - * @desc 错误色的深色悬浮态 - * @descEN The hover state of the error color. - */ - colorErrorHover: string; // 5 - - /** - * @nameZH 错误色 - * @nameEN Error color - * @desc 错误色 - * @descEN The color of the error state. - */ - colorError: string; // 6 - - /** - * @nameZH 错误色的深色激活态 - * @nameEN Error active color - * @desc 错误色的深色激活态 - * @descEN The active state of the error color. - */ - colorErrorActive: string; // 7 - - /** - * @nameZH 错误色的文本悬浮态 - * @nameEN Error text hover state - * @desc 错误色的文本悬浮态 - * @descEN The hover state of the text in the error color. - */ - colorErrorTextHover: string; // 8 - - /** - * @nameZH 错误色的文本默认态 - * @nameEN Error text default state - * @desc 错误色的文本默认态 - * @descEN The default state of the text in the error color. - */ - colorErrorText: string; // 9 - - /** - * @nameZH 错误色的文本激活态 - * @nameEN Error text active state - * @desc 错误色的文本激活态 - * @descEN The active state of the text in the error color. - */ - colorErrorTextActive: string; // 10 -} - -export interface ColorLinkMapToken { - /** - * @nameZH 超链接颜色 - * @nameEN Hyperlink color - * @desc 控制超链接的颜色。 - * @descEN Control the color of hyperlink. - */ - colorLink: string; - /** - * @nameZH 超链接悬浮颜色 - * @nameEN Hyperlink hover color - * @desc 控制超链接悬浮时的颜色。 - * @descEN Control the color of hyperlink when hovering. - */ - colorLinkHover: string; - /** - * @nameZH 超链接激活颜色 - * @nameEN Hyperlink active color - * @desc 控制超链接被点击时的颜色。 - * @descEN Control the color of hyperlink when clicked. - */ - colorLinkActive: string; -} - -export interface ColorMapToken - extends ColorNeutralMapToken, - ColorPrimaryMapToken, - ColorSuccessMapToken, - ColorWarningMapToken, - ColorErrorMapToken, - ColorInfoMapToken, - ColorLinkMapToken { - /** - * @nameZH 纯白色 - * @desc 不随主题变化的纯白色 - * @descEN Pure white color don't changed by theme - * @default #FFFFFF - */ - colorWhite: string; - - /** - * @nameZH 浮层的背景蒙层颜色 - * @nameEN Background color of the mask - * @desc 浮层的背景蒙层颜色,用于遮罩浮层下面的内容,Modal、Drawer 等组件的蒙层使用的是该 token - * @descEN The background color of the mask, used to cover the content below the mask, Modal, Drawer and other components use this token - */ - colorBgMask: string; - - /** - * @nameZH 纯黑色 - * @desc 不随主题变化的纯黑色 - * @default #0000 - */ -} diff --git a/packages/x-markdown/src/plugins/theme/interface/maps/font.ts b/packages/x-markdown/src/plugins/theme/interface/maps/font.ts deleted file mode 100644 index 40ed00716..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/maps/font.ts +++ /dev/null @@ -1,139 +0,0 @@ -export interface FontMapToken { - // Font Size - /** - * @desc 小号字体大小 - * @descEN Small font size - */ - fontSizeSM: number; - /** - * @desc 标准字体大小 - * @descEN Standard font size - */ - fontSize: number; - /** - * @desc 大号字体大小 - * @descEN Large font size - */ - fontSizeLG: number; - /** - * @desc 超大号字体大小 - * @descEN Super large font size - */ - fontSizeXL: number; - - /** - * @nameZH 一级标题字号 - * @nameEN Font size of heading level 1 - * @desc H1 标签所使用的字号 - * @descEN Font size of h1 tag. - * @default 38 - */ - fontSizeHeading1: number; - /** - * @nameZH 二级标题字号 - * @nameEN Font size of heading level 2 - * @desc h2 标签所使用的字号 - * @descEN Font size of h2 tag. - * @default 30 - */ - fontSizeHeading2: number; - /** - * @nameZH 三级标题字号 - * @nameEN Font size of heading level 3 - * @desc h3 标签使用的字号 - * @descEN Font size of h3 tag. - * @default 24 - */ - fontSizeHeading3: number; - /** - * @nameZH 四级标题字号 - * @nameEN Font size of heading level 4 - * @desc h4 标签使用的字号 - * @descEN Font size of h4 tag. - * @default 20 - */ - fontSizeHeading4: number; - /** - * @nameZH 五级标题字号 - * @nameEN Font size of heading level 5 - * @desc h5 标签使用的字号 - * @descEN Font size of h5 tag. - * @default 16 - */ - fontSizeHeading5: number; - - // LineHeight - /** - * @desc 文本行高 - * @descEN Line height of text. - */ - lineHeight: number; - /** - * @desc 大型文本行高 - * @descEN Line height of large text. - */ - lineHeightLG: number; - /** - * @desc 小型文本行高 - * @descEN Line height of small text. - */ - lineHeightSM: number; - - // TextHeight - /** - * Round of fontSize * lineHeight - * @internal - */ - fontHeight: number; - /** - * Round of fontSizeSM * lineHeightSM - * @internal - */ - fontHeightSM: number; - /** - * Round of fontSizeLG * lineHeightLG - * @internal - */ - fontHeightLG: number; - - /** - * @nameZH 一级标题行高 - * @nameEN Line height of heading level 1 - * @desc H1 标签所使用的行高 - * @descEN Line height of h1 tag. - * @default 1.4 - */ - lineHeightHeading1: number; - /** - * @nameZH 二级标题行高 - * @nameEN Line height of heading level 2 - * @desc h2 标签所使用的行高 - * @descEN Line height of h2 tag. - * @default 1.35 - */ - lineHeightHeading2: number; - /** - * @nameZH 三级标题行高 - * @nameEN Line height of heading level 3 - * @desc h3 标签所使用的行高 - * @descEN Line height of h3 tag. - * @default 1.3 - */ - lineHeightHeading3: number; - /** - * @nameZH 四级标题行高 - * @nameEN Line height of heading level 4 - * @desc h4 标签所使用的行高 - * @descEN Line height of h4 tag. - * @default 1.25 - */ - lineHeightHeading4: number; - /** - * @nameZH 五级标题行高 - * @nameEN Line height of heading level 5 - * @desc h5 标签所使用的行高 - * @descEN Line height of h5 tag. - * @default 1.2 - */ - lineHeightHeading5: number; -} diff --git a/packages/x-markdown/src/plugins/theme/interface/maps/index.ts b/packages/x-markdown/src/plugins/theme/interface/maps/index.ts deleted file mode 100644 index 29d3f53d7..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/maps/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { ColorPalettes, LegacyColorPalettes } from '../presetColors'; -import type { SeedToken } from '../seeds'; -import type { ColorMapToken } from './colors'; -import type { FontMapToken } from './font'; -import type { HeightMapToken, SizeMapToken } from './size'; -import type { StyleMapToken } from './style'; - -export * from './colors'; -export * from './font'; -export * from './size'; -export * from './style'; - -export interface CommonMapToken extends StyleMapToken { - // Motion - /** - * @desc 动效播放速度,快速。用于小型元素动画交互 - * @descEN Motion speed, fast speed. Used for small element animation interaction. - */ - motionDurationFast: string; - /** - * @desc 动效播放速度,中速。用于中型元素动画交互 - * @descEN Motion speed, medium speed. Used for medium element animation interaction. - */ - motionDurationMid: string; - /** - * @desc 动效播放速度,慢速。用于大型元素如面板动画交互 - * @descEN Motion speed, slow speed. Used for large element animation interaction. - */ - motionDurationSlow: string; -} - -// ====================================================================== -// == Map Token == -// ====================================================================== -// 🔥🔥🔥🔥🔥🔥🔥 DO NOT MODIFY THIS. PLEASE CONTACT DESIGNER. 🔥🔥🔥🔥🔥🔥🔥 - -export interface MapToken - extends SeedToken, - ColorPalettes, - LegacyColorPalettes, - ColorMapToken, - SizeMapToken, - HeightMapToken, - StyleMapToken, - FontMapToken, - CommonMapToken {} diff --git a/packages/x-markdown/src/plugins/theme/interface/maps/size.ts b/packages/x-markdown/src/plugins/theme/interface/maps/size.ts deleted file mode 100644 index f69f5a927..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/maps/size.ts +++ /dev/null @@ -1,74 +0,0 @@ -export interface SizeMapToken { - /** - * @nameZH XXL - * @default 48 - */ - sizeXXL: number; - /** - * @nameZH XL - * @default 32 - */ - sizeXL: number; - /** - * @nameZH LG - * @default 24 - */ - sizeLG: number; - /** - * @nameZH MD - * @default 20 - */ - sizeMD: number; - /** Same as size by default, but could be larger in compact mode */ - sizeMS: number; - /** - * @nameZH 默认 - * @desc 默认尺寸 - * @default 16 - */ - size: number; - /** - * @nameZH SM - * @default 12 - */ - sizeSM: number; - /** - * @nameZH XS - * @default 8 - */ - sizeXS: number; - /** - * @nameZH XXS - * @default 4 - */ - sizeXXS: number; -} - -export interface HeightMapToken { - // Control - /** Only Used for control inside component like Multiple Select inner selection item */ - - /** - * @nameZH 更小的组件高度 - * @nameEN XS component height - * @desc 更小的组件高度 - * @descEN XS component height - */ - controlHeightXS: number; - - /** - * @nameZH 较小的组件高度 - * @nameEN SM component height - * @desc 较小的组件高度 - * @descEN SM component height - */ - controlHeightSM: number; - - /** - * @nameZH 较高的组件高度 - * @nameEN LG component height - * @desc 较高的组件高度 - * @descEN LG component height - */ - controlHeightLG: number; -} diff --git a/packages/x-markdown/src/plugins/theme/interface/maps/style.ts b/packages/x-markdown/src/plugins/theme/interface/maps/style.ts deleted file mode 100644 index e4faf7dac..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/maps/style.ts +++ /dev/null @@ -1,43 +0,0 @@ -export interface StyleMapToken { - /** - * @nameZH 线宽 - * @nameEN Line Width - * @desc 描边类组件的默认线宽,如 Button、Input、Select 等输入类控件。 - * @descEN The default line width of the outline class components, such as Button, Input, Select, etc. - * @default 1 - */ - lineWidthBold: number; - - /** - * @nameZH XS号圆角 - * @nameEN XS Border Radius - * @desc XS号圆角,用于组件中的一些小圆角,如 Segmented 、Arrow 等一些内部圆角的组件样式中。 - * @descEN XS size border radius, used in some small border radius components, such as Segmented, Arrow and other components with small border radius. - * @default 2 - */ - borderRadiusXS: number; - /** - * @nameZH SM号圆角 - * @nameEN SM Border Radius - * @desc SM号圆角,用于组件小尺寸下的圆角,如 Button、Input、Select 等输入类控件在 small size 下的圆角 - * @descEN SM size border radius, used in small size components, such as Button, Input, Select and other input components in small size - * @default 4 - */ - borderRadiusSM: number; - /** - * @nameZH LG号圆角 - * @nameEN LG Border Radius - * @desc LG号圆角,用于组件中的一些大圆角,如 Card、Modal 等一些组件样式。 - * @descEN LG size border radius, used in some large border radius components, such as Card, Modal and other components. - * @default 8 - */ - borderRadiusLG: number; - /** - * @nameZH 外部圆角 - * @nameEN Outer Border Radius - * @default 4 - * @desc 外部圆角 - * @descEN Outer border radius - */ - borderRadiusOuter: number; -} diff --git a/packages/x-markdown/src/plugins/theme/interface/presetColors.ts b/packages/x-markdown/src/plugins/theme/interface/presetColors.ts deleted file mode 100644 index e160935e6..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/presetColors.ts +++ /dev/null @@ -1,32 +0,0 @@ -export const PresetColors = [ - 'blue', - 'purple', - 'cyan', - 'green', - 'magenta', - 'pink', - 'red', - 'orange', - 'yellow', - 'volcano', - 'geekblue', - 'lime', - 'gold', -] as const; - -export type PresetColorKey = (typeof PresetColors)[number]; - -export type PresetColorType = Record; - -type ColorPaletteKeyIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; - -export type LegacyColorPalettes = { - /** - * @deprecated - */ - [key in `${keyof PresetColorType}-${ColorPaletteKeyIndex}`]: string; -}; - -export type ColorPalettes = { - [key in `${keyof PresetColorType}${ColorPaletteKeyIndex}`]: string; -}; diff --git a/packages/x-markdown/src/plugins/theme/interface/seeds.ts b/packages/x-markdown/src/plugins/theme/interface/seeds.ts deleted file mode 100644 index 98a185ed1..000000000 --- a/packages/x-markdown/src/plugins/theme/interface/seeds.ts +++ /dev/null @@ -1,282 +0,0 @@ -import type { PresetColorType } from './presetColors'; - -// ====================================================================== -// == Seed Token == -// ====================================================================== -// 🔥🔥🔥🔥🔥🔥🔥 DO NOT MODIFY THIS. PLEASE CONTACT DESIGNER. 🔥🔥🔥🔥🔥🔥🔥 - -export interface SeedToken extends PresetColorType { - // ---------- Color ---------- // - - /** - * @nameZH 品牌主色 - * @nameEN Brand Color - * @desc 品牌色是体现产品特性和传播理念最直观的视觉元素之一。在你完成品牌主色的选取之后,我们会自动帮你生成一套完整的色板,并赋予它们有效的设计语义 - * @descEN Brand color is one of the most direct visual elements to reflect the characteristics and communication of the product. After you have selected the brand color, we will automatically generate a complete color palette and assign it effective design semantics. - */ - colorPrimary: string; - - /** - * @nameZH 成功色 - * @nameEN Success Color - * @desc 用于表示操作成功的 Token 序列,如 Result、Progress 等组件会使用该组梯度变量。 - * @descEN Used to represent the token sequence of operation success, such as Result, Progress and other components will use these map tokens. - */ - colorSuccess: string; - - /** - * @nameZH 警戒色 - * @nameEN Warning Color - * @desc 用于表示操作警告的 Token 序列,如 Notification、 Alert等警告类组件或 Input 输入类等组件会使用该组梯度变量。 - * @descEN Used to represent the warning map token, such as Notification, Alert, etc. Alert or Control component(like Input) will use these map tokens. - */ - colorWarning: string; - - /** - * @nameZH 错误色 - * @nameEN Error Color - * @desc 用于表示操作失败的 Token 序列,如失败按钮、错误状态提示(Result)组件等。 - * @descEN Used to represent the visual elements of the operation failure, such as the error Button, error Result component, etc. - */ - colorError: string; - - /** - * @nameZH 信息色 - * @nameEN Info Color - * @desc 用于表示操作信息的 Token 序列,如 Alert 、Tag、 Progress 等组件都有用到该组梯度变量。 - * @descEN Used to represent the operation information of the Token sequence, such as Alert, Tag, Progress, and other components use these map tokens. - */ - colorInfo: string; - - /** - * @nameZH 基础文本色 - * @nameEN Seed Text Color - * @desc 用于派生文本色梯度的基础变量,v5 中我们添加了一层文本色的派生算法可以产出梯度明确的文本色的梯度变量。但请不要在代码中直接使用该 Seed Token ! - * @descEN Used to derive the base variable of the text color gradient. In v5, we added a layer of text color derivation algorithm to produce gradient variables of text color gradient. But please do not use this Seed Token directly in the code! - */ - colorTextBase: string; - - /** - * @nameZH 基础背景色 - * @nameEN Seed Background Color - * @desc 用于派生背景色梯度的基础变量,v5 中我们添加了一层背景色的派生算法可以产出梯度明确的背景色的梯度变量。但请不要在代码中直接使用该 Seed Token ! - * @descEN Used to derive the base variable of the background color gradient. In v5, we added a layer of background color derivation algorithm to produce map token of background color. But PLEASE DO NOT USE this Seed Token directly in the code! - */ - colorBgBase: string; - - /** - * @nameZH 超链接颜色 - * @nameEN Hyperlink color - * @desc 控制超链接的颜色。 - * @descEN Control the color of hyperlink. - */ - colorLink: string; - - // ---------- Font ---------- // - - /** - * @nameZH 字体 - * @nameEN Font family for default text - * @desc Ant Design 的字体家族中优先使用系统默认的界面字体,同时提供了一套利于屏显的备用字体库,来维护在不同平台以及浏览器的显示下,字体始终保持良好的易读性和可读性,体现了友好、稳定和专业的特性。 - * @descEN The font family of Ant Design prioritizes the default interface font of the system, and provides a set of alternative font libraries that are suitable for screen display to maintain the readability and readability of the font under different platforms and browsers, reflecting the friendly, stable and professional characteristics. - */ - fontFamily: string; - - /** - * @nameZH 代码字体 - * @nameEN Font family for code text - * @desc 代码字体,用于 Typography 内的 code、pre 和 kbd 类型的元素 - * @descEN Code font, used for code, pre and kbd elements in Typography - */ - fontFamilyCode: string; - - /** - * @nameZH 默认字号 - * @nameEN Default Font Size - * @desc 设计系统中使用最广泛的字体大小,文本梯度也将基于该字号进行派生。 - * @descEN The most widely used font size in the design system, from which the text gradient will be derived. - * @default 14 - */ - fontSize: number; - - // ---------- Line ---------- // - - /** - * @nameZH 基础线宽 - * @nameEN Base Line Width - * @desc 用于控制组件边框、分割线等的宽度 - * @descEN Border width of base components - */ - lineWidth: number; - - /** - * @nameZH 线条样式 - * @nameEN Line Style - * @desc 用于控制组件边框、分割线等的样式,默认是实线 - * @descEN Border style of base components - */ - lineType: string; - - // ---------- BorderRadius ---------- // - - /** - * @nameZH 基础圆角 - * @nameEN Base Border Radius - * @descEN Border radius of base components - * @desc 基础组件的圆角大小,例如按钮、输入框、卡片等 - */ - borderRadius: number; - - // ---------- Size ---------- // - - /** - * @nameZH 尺寸变化单位 - * @nameEN Size Change Unit - * @desc 用于控制组件尺寸的变化单位,在 Ant Design 中我们的基础单位为 4 ,便于更加细致地控制尺寸梯度 - * @descEN The unit of size change, in Ant Design, our base unit is 4, which is more fine-grained control of the size step - * @default 4 - */ - sizeUnit: number; - - /** - * @nameZH 尺寸步长 - * @nameEN Size Base Step - * @desc 用于控制组件尺寸的基础步长,尺寸步长结合尺寸变化单位,就可以派生各种尺寸梯度。通过调整步长即可得到不同的布局模式,例如 V5 紧凑模式下的尺寸步长为 2 - * @descEN The base step of size change, the size step combined with the size change unit, can derive various size steps. By adjusting the step, you can get different layout modes, such as the size step of the compact mode of V5 is 2 - * @default 4 - */ - sizeStep: number; - - /** - * @nameZH 组件箭头尺寸 - * @desc 组件箭头的尺寸 - * @descEN The size of the component arrow - */ - sizePopupArrow: number; - - /** - * @nameZH 基础高度 - * @nameEN Base Control Height - * @desc Ant Design 中按钮和输入框等基础控件的高度 - * @descEN The height of the basic controls such as buttons and input boxes in Ant Design - * @default 32 - */ - controlHeight: number; - - // ---------- zIndex ---------- // - - /** - * @nameZH 基础 zIndex - * @nameEN Base zIndex - * @desc 所有组件的基础 Z 轴值,用于一些悬浮类的组件的可以基于该值 Z 轴控制层级,例如 BackTop、 Affix 等 - * @descEN The base Z axis value of all components, which can be used to control the level of some floating components based on the Z axis value, such as BackTop, Affix, etc. - * - * @default 0 - */ - zIndexBase: number; - - /** - * @nameZH 浮层基础 zIndex - * @nameEN popup base zIndex - * @desc 浮层类组件的基础 Z 轴值,用于一些悬浮类的组件的可以基于该值 Z 轴控制层级,例如 FloatButton、 Affix、Modal 等 - * @descEN Base zIndex of component like FloatButton, Affix which can be cover by large popup - * @default 1000 - */ - zIndexPopupBase: number; - - // ---------- Opacity ---------- // - - /** - * @nameZH 图片不透明度 - * @nameEN Define default Image opacity. Useful when in dark-like theme - * @desc 控制图片不透明度 - * @descEN Control image opacity - */ - opacityImage: number; - - // ---------- motion ---------- // - // TODO: 缺一个懂 motion 的人来收敛 Motion 相关的 Token - - /** - * @nameZH 动画时长变化单位 - * @nameEN Animation Duration Unit - * @desc 用于控制动画时长的变化单位 - * @descEN The unit of animation duration change - * @default 100ms - */ - motionUnit: number; - - /** - * @nameZH 动画基础时长。 - * @nameEN Animation Base Duration. - */ - motionBase: number; - - /** - * @desc 预设动效曲率 - * @descEN Preset motion curve. - */ - motionEaseOutCirc: string; - - /** - * @desc 预设动效曲率 - * @descEN Preset motion curve. - */ - motionEaseInOutCirc: string; - - /** - * @desc 预设动效曲率 - * @descEN Preset motion curve. - */ - motionEaseInOut: string; - - /** - * @desc 预设动效曲率 - * @descEN Preset motion curve. - */ - motionEaseOutBack: string; - - /** - * @desc 预设动效曲率 - * @descEN Preset motion curve. - */ - motionEaseInBack: string; - - /** - * @desc 预设动效曲率 - * @descEN Preset motion curve. - */ - motionEaseInQuint: string; - - /** - * @desc 预设动效曲率 - * @descEN Preset motion curve. - */ - motionEaseOutQuint: string; - - /** - * @desc 预设动效曲率 - * @descEN Preset motion curve. - */ - motionEaseOut: string; - - // ---------- Style ---------- // - - /** - * @nameZH 线框风格 - * @nameEN Wireframe Style - * @desc 用于将组件的视觉效果变为线框化,如果需要使用 V4 的效果,需要开启配置项 - * @descEN Used to change the visual effect of the component to wireframe, if you need to use the V4 effect, you need to enable the configuration item - * @default false - */ - wireframe: boolean; - - /** - * @nameZH 动画风格 - * @nameEN Motion Style - * @desc 用于配置动画效果,为 `false` 时则关闭动画 - * @descEN Used to configure the motion effect, when it is `false`, the motion is turned off - * @default true - */ - motion: boolean; -} diff --git a/packages/x-markdown/src/plugins/theme/useToken.ts b/packages/x-markdown/src/plugins/theme/useToken.ts deleted file mode 100644 index 55df3cebb..000000000 --- a/packages/x-markdown/src/plugins/theme/useToken.ts +++ /dev/null @@ -1,138 +0,0 @@ -import type { Theme } from '@ant-design/cssinjs'; -import { createTheme, useCacheToken } from '@ant-design/cssinjs'; -import type { - FullToken as FullTokenTypeUtil, - GetDefaultToken as GetDefaultTokenTypeUtil, - GlobalToken as GlobalTokenTypeUtil, - TokenMapKey, -} from '@ant-design/cssinjs-utils'; -import { theme as antdTheme } from 'antd'; -import type { DesignTokenProviderProps } from 'antd/es/theme/context'; -import type { AliasToken, SeedToken } from 'antd/es/theme/internal'; -import { ignore, unitless } from 'antd/es/theme/useToken'; -import formatToken from 'antd/es/theme/util/alias'; -import React from 'react'; -import version from '../version'; -import { ComponentTokenMap } from './interface/components'; - -const defaultTheme: Theme = createTheme(antdTheme.defaultAlgorithm); - -export type GlobalToken = GlobalTokenTypeUtil; - -export type GetDefaultToken> = GetDefaultTokenTypeUtil< - ComponentTokenMap, - AliasToken, - C ->; - -export type FullToken> = FullTokenTypeUtil< - ComponentTokenMap, - AliasToken, - C ->; - -const preserve: { - [key in keyof AliasToken]?: boolean; -} = { - screenXS: true, - screenXSMin: true, - screenXSMax: true, - screenSM: true, - screenSMMin: true, - screenSMMax: true, - screenMD: true, - screenMDMin: true, - screenMDMax: true, - screenLG: true, - screenLGMin: true, - screenLGMax: true, - screenXL: true, - screenXLMin: true, - screenXLMax: true, - screenXXL: true, - screenXXLMin: true, -}; - -export const getComputedToken = ( - originToken: SeedToken, - overrideToken: DesignTokenProviderProps['components'] & { - override?: Partial; - }, - theme: Theme, -) => { - const derivativeToken = theme.getDerivativeToken(originToken); - - const { override, ...components } = overrideToken; - - // Merge with override - let mergedDerivativeToken = { - ...derivativeToken, - override, - }; - - // Format if needed - mergedDerivativeToken = formatToken(mergedDerivativeToken); - - if (components) { - Object.entries(components).forEach(([key, value]) => { - const { theme: componentTheme, ...componentTokens } = value; - let mergedComponentToken = componentTokens; - if (componentTheme) { - mergedComponentToken = getComputedToken( - { - ...mergedDerivativeToken, - ...componentTokens, - }, - { - override: componentTokens, - }, - componentTheme as unknown as Theme, - ); - } - mergedDerivativeToken[key] = mergedComponentToken; - }); - } - - return mergedDerivativeToken; -}; - -export function useInternalToken(): [ - theme: Theme, - token: GlobalToken, - hashId: string, - realToken: GlobalToken, - cssVar?: DesignTokenProviderProps['cssVar'], -] { - const { - token: rootDesignToken, - hashed, - theme = defaultTheme, - override, - cssVar: ctxCssVar, - } = React.useContext(antdTheme._internalContext); - - const cssVar = { - prefix: ctxCssVar?.prefix || 'ant', - key: ctxCssVar?.key || 'css-var-root', - }; - - const mergedTheme = theme || defaultTheme; - - const [token, hashId, realToken] = useCacheToken( - mergedTheme as Theme, - [antdTheme.defaultSeed, rootDesignToken], - { - salt: `${version}-${hashed || ''}`, - override, - getComputedToken, - cssVar: { - ...cssVar, - unitless, - ignore, - preserve, - }, - }, - ); - - return [theme as Theme, realToken, hashed ? hashId : '', token, cssVar]; -} diff --git a/packages/x-markdown/src/plugins/type.ts b/packages/x-markdown/src/plugins/type.ts index da242f1b3..d7af0c366 100644 --- a/packages/x-markdown/src/plugins/type.ts +++ b/packages/x-markdown/src/plugins/type.ts @@ -1,71 +1,15 @@ import type { KatexOptions } from 'katex'; import type { TokenizerAndRendererExtension } from 'marked'; -import type { ReactNode } from 'react'; -import type { SyntaxHighlighterProps } from 'react-syntax-highlighter'; export type LatexOption = { katexOptions?: KatexOptions; replaceAlignStart?: boolean; }; -type HighlightCodeType = 'root' | 'header' | 'headerTitle' | 'code'; -export type HighlightCodeProps = { - lang?: string; - children: string; - header?: ReactNode | null; - prefixCls?: string; - style?: React.CSSProperties; - className?: string; - highlightProps?: Partial; - // Semantic - classNames?: Partial>; - styles?: Partial>; -}; - -type MermaidType = 'root' | 'header' | 'graph' | 'code'; -export type MermaidProps = { - children: string; - header?: ReactNode | null; - prefixCls?: string; - style?: React.CSSProperties; - className?: string; - highlightProps?: Partial; - // Semantic - classNames?: Partial>; - styles?: Partial>; -}; - -interface BaseComponentConfig { - style: React.CSSProperties; - styles: Record; - className: string; - classNames: Record; -} - -type ComponentConfig< - CompProps extends Record, - PickType extends keyof CompProps = keyof BaseComponentConfig, -> = Pick; - -export interface MarkdownComponentsConfig { - highlightCode?: ComponentConfig; - mermaid?: ComponentConfig; -} - export type PluginsType = { /** * @desc 渲染数学公式Latex语法。 * @descEN Rendering mathematical formulas using Latex syntax. */ Latex: (options?: LatexOption) => TokenizerAndRendererExtension[]; - /** - * @desc 渲染代码高亮。 - * @descEN Highlight the rendering code. - */ - HighlightCode: (props: HighlightCodeProps) => React.ReactNode; - /** - * @desc 渲染 Mermaid 图表。 - * @descEN Rendering the Mermaid Chart. - */ - Mermaid: (props: MermaidProps) => React.ReactNode; }; diff --git a/packages/x-markdown/src/plugins/version/plugin-meta.json b/packages/x-markdown/src/plugins/version/plugin-meta.json new file mode 100644 index 000000000..eda9d9992 --- /dev/null +++ b/packages/x-markdown/src/plugins/version/plugin-meta.json @@ -0,0 +1,7 @@ +[ + { + "plugin": "Latex", + "desc": "渲染数学公式Latex语法。", + "descEn": "Rendering mathematical formulas using Latex syntax." + } +] diff --git a/packages/x-markdown/tests/dekko/dist.test.ts b/packages/x-markdown/tests/dekko/dist.test.ts index 1b1ed7605..25780f332 100644 --- a/packages/x-markdown/tests/dekko/dist.test.ts +++ b/packages/x-markdown/tests/dekko/dist.test.ts @@ -13,13 +13,7 @@ $('dist/plugins') .isDirectory() .hasFile('latex.js') .hasFile('latex.min.js') - .hasFile('latex.min.js.map') - .hasFile('mermaid.js') - .hasFile('mermaid.min.js') - .hasFile('mermaid.min.js.map') - .hasFile('code-high-light.js') - .hasFile('code-high-light.min.js') - .hasFile('code-high-light.min.js.map'); + .hasFile('latex.min.js.map'); // eslint-disable-next-line no-console console.log(chalk.green('✨ `dist/plugins` directory is valid.')); diff --git a/packages/x-markdown/tests/dekko/lib.test.ts b/packages/x-markdown/tests/dekko/lib.test.ts index 013e50fbf..80ec54b29 100644 --- a/packages/x-markdown/tests/dekko/lib.test.ts +++ b/packages/x-markdown/tests/dekko/lib.test.ts @@ -35,16 +35,12 @@ $('lib/plugins/*') (filename: string) => !filename.endsWith('type.js') && !filename.endsWith('type.d.ts') && - !filename.endsWith('hooks') && - !filename.endsWith('theme'), + !filename.endsWith('hooks'), ) .isDirectory() .hasFile('index.js') .hasFile('index.d.ts'); -// theme -$('lib/plugins/theme').isDirectory(); - // eslint-disable-next-line no-console console.log(chalk.green('✨ `lib/plugins` directory is valid.')); diff --git a/packages/x-markdown/tests/setup.ts b/packages/x-markdown/tests/setup.ts index deeaa1ba2..66ba0f289 100644 --- a/packages/x-markdown/tests/setup.ts +++ b/packages/x-markdown/tests/setup.ts @@ -40,7 +40,7 @@ export function fillWindowEnv(window: Window | DOMWindow) { }); } - // Fix css-animation or rc-motion deps on these + // Fix css-animation or @rc-component/motion deps on these // https://github.com/react-component/motion/blob/9c04ef1a210a4f3246c9becba6e33ea945e00669/src/util/motion.ts#L27-L35 // https://github.com/yiminghe/css-animation/blob/a5986d73fd7dfce75665337f39b91483d63a4c8c/src/Event.js#L44 win.AnimationEvent = win.AnimationEvent || win.Event; diff --git a/packages/x-markdown/tests/setupAfterEnv.ts b/packages/x-markdown/tests/setupAfterEnv.ts index cb39bc909..d30ddfa22 100644 --- a/packages/x-markdown/tests/setupAfterEnv.ts +++ b/packages/x-markdown/tests/setupAfterEnv.ts @@ -1,9 +1,9 @@ import '@testing-library/jest-dom'; +import { spyElementPrototypes } from '@rc-component/util/lib/test/domHook'; import { toHaveNoViolations } from 'jest-axe'; import jsdom from 'jsdom'; import format, { plugins } from 'pretty-format'; -import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; // Mock `scrollTo` since jsdom do not support it spyElementPrototypes(HTMLElement, { diff --git a/packages/x-sdk/.jest.js b/packages/x-sdk/.jest.js index df1b1f6e7..12cb73d54 100644 --- a/packages/x-sdk/.jest.js +++ b/packages/x-sdk/.jest.js @@ -1,4 +1,5 @@ const compileModules = [ + '@rc-component', 'react-sticky-box', 'rc-tween-one', '@babel', diff --git a/packages/x-sdk/package.json b/packages/x-sdk/package.json index 251ac1665..8f947cff5 100644 --- a/packages/x-sdk/package.json +++ b/packages/x-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@ant-design/x-sdk", - "version": "2.0.1", + "version": "2.1.1", "homepage": "https://x.ant.design/x-sdks/introduce", "bugs": { "url": "https://github.com/ant-design/x/issues" @@ -43,7 +43,9 @@ ], "license": "MIT", "description": "placeholder for @ant-design/x-sdk", - "dependencies": {}, + "dependencies": { + "@rc-component/util": "^1.4.0" + }, "devDependencies": { "@types/react": "^19.0.2", "@types/react-dom": "^19.0.2", diff --git a/packages/x-sdk/src/x-chat/__test__/store.test.ts b/packages/x-sdk/src/x-chat/__test__/store.test.ts new file mode 100644 index 000000000..65c2939c2 --- /dev/null +++ b/packages/x-sdk/src/x-chat/__test__/store.test.ts @@ -0,0 +1,168 @@ +import { ChatMessagesStore } from '../store'; + +describe('ChatMessagesStore', () => { + let store: ChatMessagesStore<{ id: string; message: string }>; + + beforeEach(() => { + store = new ChatMessagesStore<{ id: string; message: string }>([]); + jest.useFakeTimers(); + }); + + afterEach(() => { + store.destroy(); + jest.clearAllTimers(); + jest.useRealTimers(); + }); + + describe('destroy method', () => { + it('should clear throttle timer when destroy is called', () => { + // 设置一些消息来触发throttle逻辑 + store.setMessages([{ id: '1', message: 'test' }]); + + // 快速连续调用以触发throttle + store.setMessages([{ id: '1', message: 'test1' }]); + store.setMessages([{ id: '1', message: 'test2' }]); + + // 验证throttleTimer被设置 + expect((store as any).throttleTimer).toBeTruthy(); + expect((store as any).pendingEmit).toBe(true); + + // 调用destroy + store.destroy(); + + // 验证状态被清理 + expect((store as any).throttleTimer).toBeNull(); + expect((store as any).pendingEmit).toBe(false); + expect((store as any).listeners).toEqual([]); + }); + + it('should not throw error when destroy is called without active timer', () => { + // 确保没有活跃的timer + expect((store as any).throttleTimer).toBeNull(); + + // 调用destroy不应抛出错误 + expect(() => { + store.destroy(); + }).not.toThrow(); + + // 验证状态被正确设置 + expect((store as any).throttleTimer).toBeNull(); + expect((store as any).pendingEmit).toBe(false); + expect((store as any).listeners).toEqual([]); + }); + + it('should clear listeners array when destroy is called', () => { + // 添加一些监听器 + const listener1 = jest.fn(); + const listener2 = jest.fn(); + + store.subscribe(listener1); + store.subscribe(listener2); + + expect((store as any).listeners).toHaveLength(2); + + // 调用destroy + store.destroy(); + + // 验证listeners被清空 + expect((store as any).listeners).toEqual([]); + }); + }); + + describe('throttledEmitListeners with pendingEmit', () => { + it('should flush pending updates when throttle timer expires', () => { + const listener = jest.fn(); + store.subscribe(listener); + + // 快速连续调用以触发throttle + store.setMessages([{ id: '1', message: 'test1' }]); + store.setMessages([{ id: '1', message: 'test2' }]); + store.setMessages([{ id: '1', message: 'test3' }]); + + // 验证pendingEmit为true + expect((store as any).pendingEmit).toBe(true); + expect(listener).toHaveBeenCalledTimes(1); // 第一次立即触发 + + // 快进时间让throttle timer触发 + jest.runAllTimers(); + + // 验证pendingEmit被重置且监听器被再次调用 + expect((store as any).pendingEmit).toBe(false); + expect(listener).toHaveBeenCalledTimes(2); // 第一次立即触发 + 第二次flush + }); + + it('should not flush pending updates when destroy is called before timer expires', () => { + const listener = jest.fn(); + store.subscribe(listener); + + // 触发throttle + store.setMessages([{ id: '1', message: 'test1' }]); + store.setMessages([{ id: '1', message: 'test2' }]); + + expect((store as any).pendingEmit).toBe(true); + expect(listener).toHaveBeenCalledTimes(1); + + // 在timer到期前调用destroy + store.destroy(); + + // 快进时间 + jest.runAllTimers(); + + // 验证没有额外的监听器调用 + expect(listener).toHaveBeenCalledTimes(1); + expect((store as any).pendingEmit).toBe(false); + }); + }); + + describe('subscribe cleanup', () => { + it('should clear throttle timer when last listener unsubscribes', () => { + const listener = jest.fn(); + const unsubscribe = store.subscribe(listener); + + // 触发throttle + store.setMessages([{ id: '1', message: 'test' }]); + store.setMessages([{ id: '1', message: 'test2' }]); + + expect((store as any).throttleTimer).toBeTruthy(); + expect((store as any).pendingEmit).toBe(true); + + // 取消订阅最后一个监听器 + unsubscribe(); + + // 验证timer被清理 + expect((store as any).throttleTimer).toBeNull(); + expect((store as any).pendingEmit).toBe(false); + }); + }); + + describe('edge cases', () => { + it('should handle multiple destroy calls gracefully', () => { + // 第一次调用 + store.destroy(); + + // 第二次调用不应抛出错误 + expect(() => { + store.destroy(); + }).not.toThrow(); + + // 第三次调用 + expect(() => { + store.destroy(); + }).not.toThrow(); + }); + + it('should handle destroy with null throttleTimer', () => { + // 确保throttleTimer为null + expect((store as any).throttleTimer).toBeNull(); + + // 设置pendingEmit为true + (store as any).pendingEmit = true; + + // 调用destroy + store.destroy(); + + // 验证pendingEmit被重置 + expect((store as any).pendingEmit).toBe(false); + }); + }); +}); diff --git a/packages/x-sdk/src/x-chat/index.ts b/packages/x-sdk/src/x-chat/index.ts index 50f074a36..f8d653cc0 100644 --- a/packages/x-sdk/src/x-chat/index.ts +++ b/packages/x-sdk/src/x-chat/index.ts @@ -1,11 +1,11 @@ -import { useEvent } from 'rc-util'; -import React, { useState } from 'react'; +import { useEvent } from '@rc-component/util'; +import React, { useEffect, useState } from 'react'; import type { AnyObject } from '../_util/type'; import { AbstractChatProvider } from '../chat-providers'; import { ConversationData } from '../x-conversations'; import { AbstractXRequestClass } from '../x-request'; import type { SSEOutput } from '../x-stream'; -import { useChatStore } from './store'; +import { ConversationKey, useChatStore } from './store'; export type SimpleType = string | number | boolean | object; @@ -76,7 +76,8 @@ function toArray(item: T | T[]): T[] { return Array.isArray(item) ? item : [item]; } -const IsRequestingMap = new Map(); +const IsRequestingMap = new Map(); +const generateConversationKey = () => Symbol('ConversationKey'); export default function useXChat< ChatMessage extends SimpleType = string, @@ -90,13 +91,23 @@ export default function useXChat< requestPlaceholder, parser, provider, - conversationKey, + conversationKey: originalConversationKey, } = config; // ========================= Agent Messages ========================= const idRef = React.useRef(0); const requestHandlerRef = React.useRef>(undefined); const [isRequesting, setIsRequesting] = useState(false); + // fix #1431, should give a default key to create store + const [conversationKey, setConversationKey] = useState( + originalConversationKey || generateConversationKey(), + ); + + useEffect(() => { + if (originalConversationKey) { + setConversationKey(originalConversationKey); + } + }, [originalConversationKey]); const { messages, setMessages, getMessages, setMessage } = useChatStore>( () => diff --git a/packages/x-sdk/src/x-chat/store.ts b/packages/x-sdk/src/x-chat/store.ts index 268977dd4..d7e559501 100644 --- a/packages/x-sdk/src/x-chat/store.ts +++ b/packages/x-sdk/src/x-chat/store.ts @@ -1,9 +1,9 @@ import { useEffect, useState, useSyncExternalStore } from 'react'; -type ConversationKey = string | number; +export type ConversationKey = string | number | symbol; export const chatMessagesStoreHelper = { - _chatMessagesStores: new Map>(), + _chatMessagesStores: new Map>(), get: (conversationKey: ConversationKey) => { return chatMessagesStoreHelper._chatMessagesStores.get(conversationKey); }, @@ -24,21 +24,45 @@ export class ChatMessagesStore { private listeners: (() => void)[] = []; private conversationKey: ConversationKey | undefined; + // Throttle state for preventing "Maximum update depth exceeded" during streaming + private throttleTimer: ReturnType | null = null; + private pendingEmit = false; + private readonly throttleInterval: number = 50; + private emitListeners() { this.listeners.forEach((listener) => { listener(); }); } + private throttledEmitListeners() { + if (!this.throttleTimer) { + // Leading edge: execute immediately + this.emitListeners(); + this.pendingEmit = false; + + this.throttleTimer = setTimeout(() => { + this.throttleTimer = null; + // Trailing edge: flush pending updates + if (this.pendingEmit) { + this.emitListeners(); + this.pendingEmit = false; + } + }, this.throttleInterval); + } else { + this.pendingEmit = true; + } + } + constructor(defaultMessages: T[], conversationKey?: ConversationKey) { - this.setMessages(defaultMessages); + this.setMessagesInternal(defaultMessages, false); if (conversationKey) { this.conversationKey = conversationKey; chatMessagesStoreHelper.set(this.conversationKey, this); } } - setMessages = (messages: T[] | ((ori: T[]) => T[])) => { + private setMessagesInternal = (messages: T[] | ((ori: T[]) => T[]), throttle = true) => { let list: T[]; if (typeof messages === 'function') { list = messages(this.messages); @@ -46,10 +70,18 @@ export class ChatMessagesStore { list = messages as T[]; } this.messages = [...list]; - this.emitListeners(); + if (throttle) { + this.throttledEmitListeners(); + } else { + this.emitListeners(); + } return true; }; + setMessages = (messages: T[] | ((ori: T[]) => T[])) => { + return this.setMessagesInternal(messages, true); + }; + getMessages = () => { return this.messages; }; @@ -96,18 +128,40 @@ export class ChatMessagesStore { this.listeners.push(callback); return () => { this.listeners = this.listeners.filter((listener) => listener !== callback); + // Clean up throttle timer when no listeners remain to prevent memory leaks + // and "setState on unmounted component" warnings + if (this.listeners.length === 0) { + if (this.throttleTimer) { + clearTimeout(this.throttleTimer); + this.throttleTimer = null; + } + this.pendingEmit = false; + } }; }; + + /** + * Clean up resources (throttle timer) when the store is no longer needed. + * Should be called when the component unmounts or the store is disposed. + */ + destroy = () => { + if (this.throttleTimer) { + clearTimeout(this.throttleTimer); + this.throttleTimer = null; + } + this.pendingEmit = false; + this.listeners = []; + }; } type Getter = () => T; export function useChatStore( defaultValue: T[] | Getter, - conversationKey?: ConversationKey, + conversationKey: ConversationKey, ) { const createStore = () => { - if (conversationKey && chatMessagesStoreHelper.get(conversationKey)) { + if (chatMessagesStoreHelper.get(conversationKey)) { return chatMessagesStoreHelper.get(conversationKey) as ChatMessagesStore; } const messages = diff --git a/packages/x-sdk/tests/setup.ts b/packages/x-sdk/tests/setup.ts index da38b4e63..27f570d32 100644 --- a/packages/x-sdk/tests/setup.ts +++ b/packages/x-sdk/tests/setup.ts @@ -41,7 +41,7 @@ export function fillWindowEnv(window: Window | DOMWindow) { }); } - // Fix css-animation or rc-motion deps on these + // Fix css-animation or @rc-component/motion deps on these // https://github.com/react-component/motion/blob/9c04ef1a210a4f3246c9becba6e33ea945e00669/src/util/motion.ts#L27-L35 // https://github.com/yiminghe/css-animation/blob/a5986d73fd7dfce75665337f39b91483d63a4c8c/src/Event.js#L44 win.AnimationEvent = win.AnimationEvent || win.Event; diff --git a/packages/x-sdk/tests/utils.tsx b/packages/x-sdk/tests/utils.tsx index e8c7166f9..0891eb503 100644 --- a/packages/x-sdk/tests/utils.tsx +++ b/packages/x-sdk/tests/utils.tsx @@ -1,8 +1,8 @@ +import { _rs as onEsResize } from '@rc-component/resize-observer/es/utils/observerUtil'; +import { _rs as onLibResize } from '@rc-component/resize-observer/lib/utils/observerUtil'; import type { RenderOptions, RenderResult } from '@testing-library/react'; import { act, render } from '@testing-library/react'; import MockDate from 'mockdate'; -import { _rs as onEsResize } from 'rc-resize-observer/es/utils/observerUtil'; -import { _rs as onLibResize } from 'rc-resize-observer/lib/utils/observerUtil'; import type { ReactElement } from 'react'; import React, { createRef, StrictMode } from 'react'; diff --git a/packages/x/.dumi/components/SemanticPreview.tsx b/packages/x/.dumi/components/SemanticPreview.tsx index 9c38dd4ba..d01f6332a 100644 --- a/packages/x/.dumi/components/SemanticPreview.tsx +++ b/packages/x/.dumi/components/SemanticPreview.tsx @@ -1,8 +1,8 @@ import { XProvider } from '@ant-design/x'; +import set from '@rc-component/util/lib/utils/set'; import { Col, Flex, Popover, Row, Tag, Typography, theme } from 'antd'; import { createStyles, css } from 'antd-style'; -import classnames from 'classnames'; -import set from 'rc-util/lib/utils/set'; +import { clsx } from 'clsx'; /* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */ import React from 'react'; @@ -149,13 +149,13 @@ const SemanticPreview: React.FC = (props) => { // ======================== Render ======================== return ( -

      +
      - + {cloneNode} -
        +
          {semantics.map((semantic) => ( = (props) => { } >
        • setHoverSemantic(semantic.name)} onMouseLeave={() => setHoverSemantic(null)} > @@ -201,7 +201,7 @@ const SemanticPreview: React.FC = (props) => {
          = (props) => { if (!before && !after) { return ( - + {title} {subtitle && {subtitle}} {tag && ( - + {tag.replace(/VERSION/i, version)} )} diff --git a/packages/x/.dumi/hooks/useThemeAnimation.ts b/packages/x/.dumi/hooks/useThemeAnimation.ts index 391c54709..096077b91 100644 --- a/packages/x/.dumi/hooks/useThemeAnimation.ts +++ b/packages/x/.dumi/hooks/useThemeAnimation.ts @@ -1,5 +1,5 @@ +import { removeCSS, updateCSS } from '@rc-component/util/lib/Dom/dynamicCSS'; import { useTheme } from 'antd-style'; -import { removeCSS, updateCSS } from 'rc-util/lib/Dom/dynamicCSS'; import { useEffect, useRef } from 'react'; const viewTransitionStyle = ` diff --git a/packages/x/.dumi/pages/index/common/Container.tsx b/packages/x/.dumi/pages/index/common/Container.tsx index 1bf1f52b4..eac12e10c 100644 --- a/packages/x/.dumi/pages/index/common/Container.tsx +++ b/packages/x/.dumi/pages/index/common/Container.tsx @@ -1,5 +1,5 @@ import { createStyles } from 'antd-style'; -import classnames from 'classnames'; +import { clsx } from 'clsx'; import React from 'react'; const useStyle = createStyles(({ token, css }) => { @@ -48,7 +48,7 @@ const Container: React.FC = (props) => { const { styles } = useStyle(); return (
          diff --git a/packages/x/.dumi/pages/index/components/DesignGuide.tsx b/packages/x/.dumi/pages/index/components/DesignGuide.tsx index 37dfeb780..48c2a7fde 100644 --- a/packages/x/.dumi/pages/index/components/DesignGuide.tsx +++ b/packages/x/.dumi/pages/index/components/DesignGuide.tsx @@ -1,6 +1,5 @@ import { Button } from 'antd'; import { createStyles } from 'antd-style'; -import classnames from 'classnames'; import { useLocation, useNavigate } from 'dumi'; import React from 'react'; import useLocale from '../../../hooks/useLocale'; @@ -224,7 +223,7 @@ const DesignGuide: React.FC = () => { return (
          -
          +
          icon icon {
          - - @@ -275,7 +275,7 @@ const MainBanner: React.FC = () => { diff --git a/packages/x/.dumi/pages/index/components/SceneIntroduction/index.tsx b/packages/x/.dumi/pages/index/components/SceneIntroduction/index.tsx index e1fa37145..a0f2941bd 100644 --- a/packages/x/.dumi/pages/index/components/SceneIntroduction/index.tsx +++ b/packages/x/.dumi/pages/index/components/SceneIntroduction/index.tsx @@ -1,6 +1,6 @@ import { Button, Carousel } from 'antd'; import { createStyles } from 'antd-style'; -import classnames from 'classnames'; +import { clsx } from 'clsx'; import React from 'react'; import useLocale from '../../../../hooks/useLocale'; @@ -228,7 +228,7 @@ const SceneBanner: React.FC = () => {
    - - ) : null + + + } /> ); diff --git a/packages/x/components/attachments/PlaceholderUploader.tsx b/packages/x/components/attachments/PlaceholderUploader.tsx index acc957156..36d86cf15 100644 --- a/packages/x/components/attachments/PlaceholderUploader.tsx +++ b/packages/x/components/attachments/PlaceholderUploader.tsx @@ -1,5 +1,5 @@ import { Flex, GetRef, Typography, Upload, type UploadProps } from 'antd'; -import classNames from 'classnames'; +import { clsx } from 'clsx'; import React from 'react'; import { AttachmentContext } from './context'; @@ -65,7 +65,7 @@ function Placeholder(props: PlaceholderProps, ref: React.Ref>) { - const { children, upload, className, style } = props; - + const { children, upload, className, style, visible } = props; const uploadRef = React.useRef>(null); React.useImperativeHandle(ref, () => uploadRef.current!); // ============================ Render ============================ return ( - + {children} ); diff --git a/packages/x/components/attachments/__tests__/__snapshots__/demo-extend.test.ts.snap b/packages/x/components/attachments/__tests__/__snapshots__/demo-extend.test.ts.snap index 3683e735b..6e4fd263c 100644 --- a/packages/x/components/attachments/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/packages/x/components/attachments/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -8,7 +8,7 @@ exports[`renders components/attachments/demo/basic.tsx extend context correctly class="ant-flex css-var-_r_d_ ant-flex-align-flex-start ant-flex-gap-middle ant-flex-vertical" >
  • - {labelNode &&
    {labelNode}
    } + {labelNode &&
    {labelNode}
    } {mergeCollapsible && (
    = ({ className, children }) => {
    {({ className: motionClassName, style }, motionRef) => ( -
    +
    {children}
    )} diff --git a/packages/x/components/conversations/Item.tsx b/packages/x/components/conversations/Item.tsx index 663a34a33..73348c7b1 100644 --- a/packages/x/components/conversations/Item.tsx +++ b/packages/x/components/conversations/Item.tsx @@ -1,9 +1,9 @@ import { EllipsisOutlined } from '@ant-design/icons'; +import pickAttrs from '@rc-component/util/lib/pickAttrs'; import type { MenuProps } from 'antd'; import { Dropdown, Typography } from 'antd'; import type { DirectionType } from 'antd/es/config-provider'; -import classnames from 'classnames'; -import pickAttrs from 'rc-util/lib/pickAttrs'; +import { clsx } from 'clsx'; import React from 'react'; import type { ConversationsProps } from '.'; import type { ConversationItemType } from './interface'; @@ -43,7 +43,7 @@ const ConversationsItem: React.FC = (props) => { const { disabled } = info; // ============================ Style ============================= - const mergedCls = classnames( + const mergedCls = clsx( className, `${prefixCls}-item`, { [`${prefixCls}-item-active`]: active && !disabled }, diff --git a/packages/x/components/conversations/__tests__/index.test.tsx b/packages/x/components/conversations/__tests__/index.test.tsx index db039ef89..06c283f8e 100644 --- a/packages/x/components/conversations/__tests__/index.test.tsx +++ b/packages/x/components/conversations/__tests__/index.test.tsx @@ -1,4 +1,4 @@ -import KeyCode from 'rc-util/lib/KeyCode'; +import KeyCode from '@rc-component/util/lib/KeyCode'; import React from 'react'; import mountTest from '../../../tests/shared/mountTest'; import rtlTest from '../../../tests/shared/rtlTest'; @@ -140,7 +140,6 @@ describe('Conversations Component', () => { const element = container.querySelector('.ant-dropdown-open'); expect(element).not.toBeInTheDocument(); }); - describe('should handle menu trigger function', () => { it('render node', async () => { const { findAllByText, container } = render( diff --git a/packages/x/components/conversations/demo/shortcutKeys.tsx b/packages/x/components/conversations/demo/shortcutKeys.tsx index 033ba0d7d..ff242b838 100644 --- a/packages/x/components/conversations/demo/shortcutKeys.tsx +++ b/packages/x/components/conversations/demo/shortcutKeys.tsx @@ -6,9 +6,9 @@ import { SignatureOutlined, } from '@ant-design/icons'; import { Conversations, ConversationsProps } from '@ant-design/x'; +import KeyCode from '@rc-component/util/lib/KeyCode'; import type { GetProp } from 'antd'; import { Flex, Tag, theme } from 'antd'; -import KeyCode from 'rc-util/lib/KeyCode'; import React, { useState } from 'react'; const agentItems: GetProp = [ diff --git a/packages/x/components/conversations/hooks/useCreation.tsx b/packages/x/components/conversations/hooks/useCreation.tsx index 8d6421698..6f70e7458 100644 --- a/packages/x/components/conversations/hooks/useCreation.tsx +++ b/packages/x/components/conversations/hooks/useCreation.tsx @@ -1,5 +1,5 @@ import { PlusOutlined } from '@ant-design/icons'; -import classNames from 'classnames'; +import { clsx } from 'clsx'; import React from 'react'; import { useLocale } from '../../locale'; import enUS from '../../locale/en_US'; @@ -12,14 +12,12 @@ const CreationLabel: React.FC = ({ shortcutKeysIcon, prefixC const [contextLocale] = useLocale('Conversations', enUS.Conversations); const showShortcutKeys = !!shortcutKeysIcon?.length; return ( -
    +
    {contextLocale.create} {showShortcutKeys && ( - + {shortcutKeysIcon.map((keyIcon) => ( - + {keyIcon} ))} diff --git a/packages/x/components/conversations/index.en-US.md b/packages/x/components/conversations/index.en-US.md index 338b7ef9e..31c002ddf 100644 --- a/packages/x/components/conversations/index.en-US.md +++ b/packages/x/components/conversations/index.en-US.md @@ -7,8 +7,6 @@ title: Conversations description: Used to switch between multiple agents, update conversation turns, and manage conversation history cover: https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*Oj-bTbVXtpQAAAAAAAAAAAAADgCCAQ/original coverDark: https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*qwdtSKWXeikAAAAAAAAAAAAADgCCAQ/original -demo: - cols: 1 --- ## When To Use @@ -47,8 +45,8 @@ Common props ref:[Common props](/docs/react/common-props) | onActiveChange | Callback for selection change | (value: string) => void | - | - | | menu | Operation menu for conversations | ItemMenuProps\| ((value: ConversationItemType) => ItemMenuProps) | - | - | | groupable | If grouping is supported, it defaults to the `Conversation.group` field | boolean \| GroupableProps | - | - | -| shortcutKeys | Shortcut key operations | { creation?: ShortcutKeys; items?:ShortcutKeys<'number'> \| ShortcutKeys[];} | - | - | -| creation | New conversation configuration | CreationProps | - | - | +| shortcutKeys | Shortcut key operations | { creation?: ShortcutKeys; items?:ShortcutKeys<'number'> \| ShortcutKeys[];} | - | 2.0.0 | +| creation | New conversation configuration | CreationProps | - | 2.0.0 | | styles | Semantic structure styles | styles?: {creation?: React.CSSProperties;item?: React.CSSProperties;} | - | - | | classNames | Semantic structure class names | classNames?: { creation?: string; item?:string;} | - | - | | rootClassName | Root node className | string | - | - | @@ -102,10 +100,10 @@ MenuProps & { }; ``` -## Design Token +## Semantic DOM - + -``` +## Design Token -``` + diff --git a/packages/x/components/conversations/index.tsx b/packages/x/components/conversations/index.tsx index 2d9958a30..d798fc527 100644 --- a/packages/x/components/conversations/index.tsx +++ b/packages/x/components/conversations/index.tsx @@ -1,7 +1,7 @@ +import useMergedState from '@rc-component/util/lib/hooks/useMergedState'; +import pickAttrs from '@rc-component/util/lib/pickAttrs'; import { Divider } from 'antd'; -import classnames from 'classnames'; -import useMergedState from 'rc-util/lib/hooks/useMergedState'; -import pickAttrs from 'rc-util/lib/pickAttrs'; +import { clsx } from 'clsx'; import React from 'react'; import useCollapsible from '../_util/hooks/use-collapsible'; import useProxyImperativeHandle from '../_util/hooks/use-proxy-imperative-handle'; @@ -169,7 +169,7 @@ const ForwardConversations = React.forwardRef {!!creation && ( - +
      void | - | - | | menu | 会话操作菜单 | ItemMenuProps\| ((value: ConversationItemType) => ItemMenuProps) | - | - | | groupable | 是否支持分组, 开启后默认按 `Conversation.group` 字段分组 | boolean \| GroupableProps | - | - | -| shortcutKeys | 快捷键操作 | { creation?: ShortcutKeys\; items?:ShortcutKeys\<'number'\> \| ShortcutKeys\[];} | - | - | -| creation | 新会话操作配置 | CreationProps | - | - | +| shortcutKeys | 快捷键操作 | { creation?: ShortcutKeys\; items?:ShortcutKeys\<'number'\> \| ShortcutKeys\[];} | - | 2.0.0 | +| creation | 新会话操作配置 | CreationProps | - | 2.0.0 | | styles | 语义化结构 style | styles?: {creation?: React.CSSProperties;item?: React.CSSProperties;} | - | - | | classNames | 语义化结构 className | classNames?: { creation?: string; item?:string;} | - | - | | rootClassName | 根节点类名 | string | - | - | diff --git a/packages/x/components/file-card/FileCard.tsx b/packages/x/components/file-card/FileCard.tsx index 119e7b96b..68a410f6e 100644 --- a/packages/x/components/file-card/FileCard.tsx +++ b/packages/x/components/file-card/FileCard.tsx @@ -11,10 +11,10 @@ import { JavaScriptOutlined, PythonOutlined, } from '@ant-design/icons'; +import pickAttrs from '@rc-component/util/lib/pickAttrs'; import type { ImageProps, SpinProps } from 'antd'; import { Image } from 'antd'; -import classnames from 'classnames'; -import pickAttrs from 'rc-util/lib/pickAttrs'; +import { clsx } from 'clsx'; import React, { useMemo } from 'react'; import useXComponentConfig from '../_util/hooks/use-x-component-config'; import { useXProviderContext } from '../x-provider'; @@ -199,7 +199,7 @@ const FileCard: React.FC = (props) => { const [hashId, cssVarCls] = useStyle(prefixCls); - const mergedCls = classnames( + const mergedCls = clsx( prefixCls, contextConfig.className, className, @@ -255,14 +255,14 @@ const FileCard: React.FC = (props) => { if (fileType === 'image') { ContentNode = (
      {src && ( {name} = (props) => { src={src} controls style={styles.file} - className={classnames(`${prefixCls}-video`, classNames.file)} + className={clsx(`${prefixCls}-video`, classNames.file)} {...(videoProps as React.JSX.IntrinsicElements['video'])} /> ); @@ -291,7 +291,7 @@ const FileCard: React.FC = (props) => { src={src} controls style={styles.file} - className={classnames(`${prefixCls}-audio`, classNames.file)} + className={clsx(`${prefixCls}-audio`, classNames.file)} {...(audioProps as React.JSX.IntrinsicElements['audio'])} /> ); diff --git a/packages/x/components/file-card/List.tsx b/packages/x/components/file-card/List.tsx index 36a65260c..09cb39074 100644 --- a/packages/x/components/file-card/List.tsx +++ b/packages/x/components/file-card/List.tsx @@ -1,7 +1,7 @@ import { CloseCircleFilled, LeftOutlined, RightOutlined } from '@ant-design/icons'; +import { CSSMotionList } from '@rc-component/motion'; import { Button } from 'antd'; -import classnames from 'classnames'; -import { CSSMotionList } from 'rc-motion'; +import { clsx } from 'clsx'; import React from 'react'; import { useXProviderContext } from '../x-provider'; import FileCard, { SemanticType as CardSemanticType, FileCardProps } from './FileCard'; @@ -60,7 +60,7 @@ const List: React.FC = (props) => { const [pingEnd, setPingEnd] = React.useState(false); const { root: classNameRoot, card: classNameCard, ...classNameOther } = classNames; - const mergedCls = classnames( + const mergedCls = clsx( compCls, rootClassName, className, @@ -122,9 +122,9 @@ const List: React.FC = (props) => { const { root, card: _, ...other } = styles; return ( -
      +
      = (props) => { {({ key, item, className: motionCls, style: motionStyle }) => { return (
      @@ -154,7 +154,7 @@ const List: React.FC = (props) => { {...item} size={size} key={key} - className={classnames(item.className, classNameCard)} + className={clsx(item.className, classNameCard)} classNames={{ ...classNameOther, ...item.classNames }} style={{ ...item.style, ...styles?.card }} styles={other} diff --git a/packages/x/components/file-card/components/File.tsx b/packages/x/components/file-card/components/File.tsx index ba9313c8e..743ee9650 100644 --- a/packages/x/components/file-card/components/File.tsx +++ b/packages/x/components/file-card/components/File.tsx @@ -1,4 +1,4 @@ -import classnames from 'classnames'; +import { clsx } from 'clsx'; import React, { useMemo } from 'react'; import { SemanticType } from '../FileCard'; import { getSize } from '../utils'; @@ -35,7 +35,7 @@ const File: React.FC = (props) => { } = props; const compCls = `${prefixCls}-file`; - const mergedCls = classnames(compCls, classNames.file, { + const mergedCls = clsx(compCls, classNames.file, { [`${compCls}-pointer`]: !!onClick, [`${compCls}-small`]: size === 'small', }); @@ -53,19 +53,19 @@ const File: React.FC = (props) => { return (
      {icon}
      -
      +
      {name} {ext}
      {desc && (
      {desc} diff --git a/packages/x/components/file-card/components/ImageLoading.tsx b/packages/x/components/file-card/components/ImageLoading.tsx index 7ce4fc0b2..5009c6f6f 100644 --- a/packages/x/components/file-card/components/ImageLoading.tsx +++ b/packages/x/components/file-card/components/ImageLoading.tsx @@ -1,12 +1,11 @@ -import type { SpinProps } from 'antd'; import { Flex, Skeleton, Spin } from 'antd'; -import classnames from 'classnames'; +import { clsx } from 'clsx'; import React from 'react'; import { FileCardProps } from '../FileCard'; import ImageIcon from './ImageIcon'; import usePercent from './usePercent'; -export type ImageLoadingProps = SpinProps & { +export type ImageLoadingProps = { prefixCls?: string; style?: React.CSSProperties; className?: string; @@ -14,8 +13,11 @@ export type ImageLoadingProps = SpinProps & { }; const ImageLoading: React.FC = (props) => { - const { style, className, prefixCls, percent = 'auto', spinProps } = props; - const [mergedPercent, percentText] = usePercent(true, percent); + const { style, className, prefixCls, spinProps } = props; + const [mergedPercent, percentText] = usePercent( + true, + typeof spinProps?.percent === 'undefined' ? 'auto' : spinProps?.percent, + ); const mergeSinkProps = { size: 'default', showText: true, @@ -23,7 +25,7 @@ const ImageLoading: React.FC = (props) => { ...spinProps, }; return ( -
      +
      = (props) => { height: '100%', }, }} - rootClassName={classnames(`${prefixCls}-image-skeleton`)} + rootClassName={clsx(`${prefixCls}-image-skeleton`)} active > - + {mergeSinkProps.showText && (
      {percentText}
      )} diff --git a/packages/x/components/file-card/demo/image-loading.md b/packages/x/components/file-card/demo/image-loading.md index 5d6899224..9e165d711 100644 --- a/packages/x/components/file-card/demo/image-loading.md +++ b/packages/x/components/file-card/demo/image-loading.md @@ -1,7 +1,7 @@ ## zh-CN -可使用 loading,[Image](https://ant-design.antgroup.com/components/image-cn#api)、[Spin](https://ant-design.antgroup.com/components/spin-cn#api) 属性实现图片加载。 +可使用 loading、[Image](https://ant-design.antgroup.com/components/image-cn#api)、[Spin](https://ant-design.antgroup.com/components/spin-cn#api) 属性实现图片加载。 ## en-US -Can use loading,[Image](https://ant-design.antgroup.com/components/image#api)、[Spin](https://ant-design.antgroup.com/components/spin#api) props. +Can use loading,[Image](https://ant-design.antgroup.com/components/image#api),[Spin](https://ant-design.antgroup.com/components/spin#api) props. diff --git a/packages/x/components/file-card/index.en-US.md b/packages/x/components/file-card/index.en-US.md index 21a9e1368..5a9e25502 100644 --- a/packages/x/components/file-card/index.en-US.md +++ b/packages/x/components/file-card/index.en-US.md @@ -9,6 +9,7 @@ demo: cols: 1 cover: https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*pJrtTaf-bWAAAAAAAAAAAAAADgCCAQ/original coverDark: https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*6ySvTqb7XhkAAAAAAAAAAAAADgCCAQ/original +tag: 2.0.0 --- ## When To Use @@ -43,7 +44,7 @@ Common props ref:[Common props](/docs/react/common-props) | loading | Loading state | boolean | false | - | | type | File type | 'file' \| 'image' \| 'audio' \| 'video' \| string | - | - | | src | Image or file URL | string | - | - | -| mask | Mask content, image preview. This configuration is not effective. Please refer to the imageProps property | React.ReactNode | - | - | +| mask | Mask content. For `type="image"`, this is configured via `imageProps.preview.mask`,This prop only applies to non-image file types. | React.ReactNode | - | - | | icon | Custom icon | React.ReactNode \| PresetIcons | - | - | | imageProps | Image props configuration | [Image](https://ant.design/components/image-cn#api) | - | - | | videoProps | Video props configuration | Partial | - | - | diff --git a/packages/x/components/file-card/index.zh-CN.md b/packages/x/components/file-card/index.zh-CN.md index f2fd95298..94f30a6e0 100644 --- a/packages/x/components/file-card/index.zh-CN.md +++ b/packages/x/components/file-card/index.zh-CN.md @@ -8,6 +8,7 @@ subtitle: 文件卡片 description: 用卡片的形式展示文件。 cover: https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*pJrtTaf-bWAAAAAAAAAAAAAADgCCAQ/original coverDark: https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*6ySvTqb7XhkAAAAAAAAAAAAADgCCAQ/original +tag: 2.0.0 --- ## 何时使用 @@ -42,7 +43,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*6ySvTqb7XhkAAA | loading | 是否处于加载状态 | boolean | false | - | | type | 文件类型 | 'file' \| 'image' \| 'audio' \| 'video' \| string | - | - | | src | 图片或文件地址 | string | - | - | -| mask | 遮罩内容,图片预览此配置不生效,请参考 imageProps 属性 | React.ReactNode | - | - | +| mask | 遮罩内容。对于 `type="image"`,可通过 `imageProps.preview.mask` 配置,此属性仅适用于非图像文件类型。 | React.ReactNode | - | - | | icon | 自定义图标 | React.ReactNode \| PresetIcons | - | - | | imageProps | 图片属性,同 antd [Image](https://ant.design/components/image-cn#api) 属性 | ImageProps | - | - | | videoProps | 视频属性配置 | Partial | - | - | diff --git a/packages/x/components/index.ts b/packages/x/components/index.ts index f940b9d16..782fcadb9 100644 --- a/packages/x/components/index.ts +++ b/packages/x/components/index.ts @@ -6,10 +6,14 @@ export type { AttachmentsProps } from './attachments'; export { default as Attachments } from './attachments'; export type { BubbleItemType, BubbleListProps, BubbleProps } from './bubble'; export { default as Bubble } from './bubble'; +export type { CodeHighlighterProps } from './code-highlighter'; +export { default as CodeHighlighter } from './code-highlighter'; export type { ConversationItemType, ConversationsProps } from './conversations'; export { default as Conversations } from './conversations'; export type { FileCardListProps, FileCardProps } from './file-card'; export { default as FileCard } from './file-card'; +export type { MermaidProps } from './mermaid'; +export { default as Mermaid } from './mermaid'; export type { XNotificationOpenArgs } from './notification'; export { default as notification } from './notification'; export type { PromptsItemType, PromptsProps } from './prompts'; diff --git a/packages/x/components/introduce/index.en-US.md b/packages/x/components/introduce/index.en-US.md index e7787946c..0869901ad 100644 --- a/packages/x/components/introduce/index.en-US.md +++ b/packages/x/components/introduce/index.en-US.md @@ -45,8 +45,8 @@ Based on the RICH interaction paradigm, we provide a variety of atomic component - General: `Bubble` - message bubble, `Conversations` - conversation management, `Notification` - system notification - Wake-up: `Welcome` - welcome, `Prompts` - prompt set - Expression: `Sender` - input box, `Attachment` - input attachment, `Suggestion` - quick command -- Confirmation: `Think` - thinking process, `ThoughtChain` - chain of thought、`Sources` - Sources -- Feedback: `Actions` - action list, `FileCard` - file card +- Confirmation: `Think` - thinking process, `ThoughtChain` - chain of thought +- Feedback: `Actions` - action list, `FileCard` - file card, `Sources` - source citation, `CodeHighlighter` - code highlighting, `Mermaid` - chart tool - Others: `XProvider` - global config: theme, locale, etc. Here is a simple example of building a chat box using atomic components: diff --git a/packages/x/components/introduce/index.zh-CN.md b/packages/x/components/introduce/index.zh-CN.md index 4b818023d..18179017f 100644 --- a/packages/x/components/introduce/index.zh-CN.md +++ b/packages/x/components/introduce/index.zh-CN.md @@ -45,8 +45,8 @@ showImport: false - 通用: `Bubble` - 消息气泡、`Conversations` - 会话管理、`Notification` - 系统通知 - 唤醒: `Welcome` - 欢迎、`Prompts` - 提示集 - 表达: `Sender` - 发送框、`Attachment` - 输入附件、`Suggestion` - 快捷指令 -- 确认: `Think` - 思考过程、 `ThoughtChain` - 思维链、`Sources` - 来源引用 -- 反馈: `Actions` - 操作列表、`FileCard` - 文件卡片 +- 确认: `Think` - 思考过程、`ThoughtChain` - 思维链 +- 反馈: `Actions` - 操作列表、`FileCard` - 文件卡片、`Sources` - 来源引用、`CodeHighlighter` - 代码高亮、`Mermaid` - 图表工具 - 其他: `XProvider` - 全局配置:主题、国际化等 下面是使用原子组件搭建一个最简单的对话框的代码示例: diff --git a/packages/x/components/locale/en_US.ts b/packages/x/components/locale/en_US.ts index 973bb8ba7..5edc12b00 100644 --- a/packages/x/components/locale/en_US.ts +++ b/packages/x/components/locale/en_US.ts @@ -1,6 +1,6 @@ -import type { xLocale, xMarkdownLocale } from '.'; +import type { xLocale } from '.'; -const localeValues: Required = { +const localeValues: Required = { locale: 'en', Conversations: { create: 'New chat', @@ -22,8 +22,6 @@ const localeValues: Required = { editableCancel: 'Cancel', }, Mermaid: { - copySuccess: 'Copied', - copy: 'Copy code', zoomIn: 'Zoom in', zoomOut: 'Zoom out', zoomReset: 'Reset', @@ -31,10 +29,6 @@ const localeValues: Required = { code: 'Code', image: 'Image', }, - HighlightCode: { - copySuccess: 'Copied', - copy: 'Copy code', - }, }; export default localeValues; diff --git a/packages/x/components/locale/index.tsx b/packages/x/components/locale/index.tsx index dd6aa0952..50d5b760f 100644 --- a/packages/x/components/locale/index.tsx +++ b/packages/x/components/locale/index.tsx @@ -29,12 +29,7 @@ export interface xLocale { editableOk: string; editableCancel: string; }; -} - -export interface xMarkdownLocale { Mermaid?: { - copySuccess: string; - copy: string; zoomIn: string; zoomOut: string; zoomReset: string; @@ -42,12 +37,9 @@ export interface xMarkdownLocale { code: string; image: string; }; - HighlightCode?: { - copySuccess: string; - copy: string; - }; } -export type Locale = xLocale & antdLocale & xMarkdownLocale; + +export type Locale = xLocale & antdLocale; export interface LocaleProviderProps { locale: Locale; diff --git a/packages/x/components/locale/useLocale.ts b/packages/x/components/locale/useLocale.ts index d4272694c..93a4a432b 100644 --- a/packages/x/components/locale/useLocale.ts +++ b/packages/x/components/locale/useLocale.ts @@ -1,17 +1,13 @@ import type { LocaleComponentName as AntdLocaleContextProps } from 'antd/es/locale/useLocale'; import defaultAntdEnUS from 'antd/locale/en_US'; import * as React from 'react'; -import type { Locale, xLocale, xMarkdownLocale } from '.'; +import type { Locale, xLocale } from '.'; import type { LocaleContextProps } from './context'; import LocaleContext from './context'; import defaultLocaleData from './en_US'; type LocaleComponentName = Exclude; -type MarkdownComponentName = Exclude; -type mergeLocaleComponentName = - | LocaleComponentName - | AntdLocaleContextProps - | MarkdownComponentName; +type mergeLocaleComponentName = LocaleComponentName | AntdLocaleContextProps; const useLocale = ( componentName: C, defaultLocale?: Locale[C] | (() => Locale[C]), diff --git a/packages/x/components/locale/zh_CN.ts b/packages/x/components/locale/zh_CN.ts index 1f9b5820a..2192217d3 100644 --- a/packages/x/components/locale/zh_CN.ts +++ b/packages/x/components/locale/zh_CN.ts @@ -1,6 +1,6 @@ -import type { xLocale, xMarkdownLocale } from './index'; +import type { xLocale } from './index'; -const localeValues: Required = { +const localeValues: Required = { locale: 'zh-cn', Conversations: { create: '新对话', @@ -22,8 +22,6 @@ const localeValues: Required = { editableCancel: '取消', }, Mermaid: { - copySuccess: '复制成功', - copy: '复制代码', zoomIn: '放大', zoomOut: '缩小', zoomReset: '重置', @@ -31,10 +29,6 @@ const localeValues: Required = { code: '代码', image: '图片', }, - HighlightCode: { - copySuccess: '复制成功', - copy: '复制代码', - }, }; export default localeValues; diff --git a/packages/x-markdown/src/plugins/Mermaid/index.tsx b/packages/x/components/mermaid/Mermaid.tsx similarity index 83% rename from packages/x-markdown/src/plugins/Mermaid/index.tsx rename to packages/x/components/mermaid/Mermaid.tsx index cccc6a2e8..8897be05c 100644 --- a/packages/x-markdown/src/plugins/Mermaid/index.tsx +++ b/packages/x/components/mermaid/Mermaid.tsx @@ -1,31 +1,38 @@ import { DownloadOutlined, ZoomInOutlined, ZoomOutOutlined } from '@ant-design/icons'; -import useXComponentConfig from '@ant-design/x/es/_util/hooks/use-x-component-config'; -import Actions from '@ant-design/x/es/actions'; -import { ItemType } from '@ant-design/x/es/actions/interface'; -import useLocale from '@ant-design/x/es/locale/useLocale'; -import useXProviderContext from '@ant-design/x/es/x-provider/hooks/use-x-provider-context'; -import locale_EN from '@ant-design/x/locale/en_US'; import { Button, Segmented, Tooltip } from 'antd'; -import classnames from 'classnames'; +import { clsx } from 'clsx'; import throttle from 'lodash.throttle'; import mermaid from 'mermaid'; import React, { useEffect, useRef, useState } from 'react'; import SyntaxHighlighter from 'react-syntax-highlighter'; -import type { MermaidProps } from '../type'; +import useXComponentConfig from '../_util/hooks/use-x-component-config'; +import Actions from '../actions'; +import type { ItemType } from '../actions/interface'; +import locale_EN from '../locale/en_US'; +import useLocale from '../locale/useLocale'; +import { useXProviderContext } from '../x-provider'; import useStyle from './style'; +export type MermaidType = 'root' | 'header' | 'graph' | 'code'; + +export interface MermaidProps { + children: string; + header?: React.ReactNode | null; + prefixCls?: string; + style?: React.CSSProperties; + className?: string; + highlightProps?: Partial>; + // Semantic + classNames?: Partial>; + styles?: Partial>; + onRenderTypeChange?: (value: RenderType) => void; +} + enum RenderType { Code = 'code', Image = 'image', } -mermaid.initialize({ - startOnLoad: false, - securityLevel: 'strict', - theme: 'default', - fontFamily: 'monospace', -}); - let uuid = 0; const Mermaid: React.FC = React.memo((props) => { @@ -38,6 +45,7 @@ const Mermaid: React.FC = React.memo((props) => { header, children, highlightProps, + onRenderTypeChange, } = props; const [renderType, setRenderType] = useState(RenderType.Image); const [scale, setScale] = useState(1); @@ -47,6 +55,15 @@ const Mermaid: React.FC = React.memo((props) => { const containerRef = useRef(null); const id = `mermaid-${uuid++}-${children?.length || 0}`; + useEffect(() => { + mermaid.initialize({ + startOnLoad: false, + securityLevel: 'strict', + theme: 'default', + fontFamily: 'monospace', + }); + }, []); + // ============================ locale ============================ const [contextLocale] = useLocale('Mermaid', locale_EN.Mermaid); @@ -59,10 +76,10 @@ const Mermaid: React.FC = React.memo((props) => { const contextConfig = useXComponentConfig('mermaid'); // ============================ style ============================ - const mergedCls = classnames( + const mergedCls = clsx( prefixCls, contextConfig.className, - contextConfig.classNames.root, + contextConfig.classNames?.root, className, classNames.root, hashId, @@ -81,9 +98,7 @@ const Mermaid: React.FC = React.memo((props) => { if (!isValid) throw new Error('Invalid Mermaid syntax'); const newText = children.replace(/[`\s]+$/g, ''); - const { svg } = await mermaid.render(id, newText, containerRef.current); - containerRef.current.innerHTML = svg; } catch (error) { console.warn(`Mermaid render failed: ${error}`); @@ -254,12 +269,12 @@ const Mermaid: React.FC = React.memo((props) => { return (
      = React.memo((props) => { { label: contextLocale.code, value: RenderType.Code }, ]} value={renderType} - onChange={setRenderType} + onChange={(value) => { + setRenderType(value as RenderType); + onRenderTypeChange?.(value as RenderType); + }} />
      @@ -278,13 +296,13 @@ const Mermaid: React.FC = React.memo((props) => { return ( <>
      = React.memo((props) => { /> {renderType === RenderType.Code ? (
      = React.memo((props) => { return (
      {renderHeader()} {renderContent()} diff --git a/packages/x-markdown/src/plugins/Mermaid/__test__/index.test.tsx b/packages/x/components/mermaid/__tests__/index.test.tsx similarity index 67% rename from packages/x-markdown/src/plugins/Mermaid/__test__/index.test.tsx rename to packages/x/components/mermaid/__tests__/index.test.tsx index da5844b6c..d2469eacf 100644 --- a/packages/x-markdown/src/plugins/Mermaid/__test__/index.test.tsx +++ b/packages/x/components/mermaid/__tests__/index.test.tsx @@ -1,6 +1,6 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import React from 'react'; -import Mermaid from '../index'; +import Mermaid from '../Mermaid'; // Mock mermaid jest.mock('mermaid', () => ({ @@ -55,7 +55,7 @@ jest.mock('antd', () => { const mermaidContent = 'graph TD; A-->B;'; -describe('Mermaid Plugin', () => { +describe('Mermaid Component', () => { const mockMermaid = require('mermaid'); const mockParse = mockMermaid.parse as jest.Mock; const mockRender = mockMermaid.render as jest.Mock; @@ -135,12 +135,12 @@ describe('Mermaid Plugin', () => { render({mermaidContent}); - // 切换到代码视图 + // 切换到代码模式以显示复制按钮 const codeButton = screen.getByText('Code'); fireEvent.click(codeButton); - // 查找复制按钮 - 使用 aria-label 或 role - const copyButton = screen.getByLabelText('Copy'); + // 查找复制按钮 - 使用更通用的选择器 + const copyButton = screen.getByRole('button', { name: /copy/i }); fireEvent.click(copyButton); await waitFor(() => { @@ -159,11 +159,11 @@ describe('Mermaid Plugin', () => { render({mermaidContent}); - // 切换到代码视图 + // 切换到代码模式 const codeButton = screen.getByText('Code'); fireEvent.click(codeButton); - const copyButton = screen.getByLabelText('Copy'); + const copyButton = screen.getByRole('button', { name: /copy/i }); // 确保点击不会抛出错误 expect(() => fireEvent.click(copyButton)).not.toThrow(); @@ -183,21 +183,27 @@ describe('Mermaid Plugin', () => { value: mockClipboard, }); + // Mock console.error to catch the error + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + render({mermaidContent}); - // 切换到代码视图 + // 切换到代码模式 const codeButton = screen.getByText('Code'); fireEvent.click(codeButton); - const copyButton = screen.getByLabelText('Copy'); - - // 确保点击不会抛出错误 - expect(() => fireEvent.click(copyButton)).not.toThrow(); + const copyButton = screen.getByRole('button', { name: /copy/i }); + fireEvent.click(copyButton); - // 验证剪贴板被调用,即使失败也不会抛出错误 + // 等待异步操作完成 await waitFor(() => { expect(mockClipboard.writeText).toHaveBeenCalledWith('graph TD; A-->B;'); }); + + // 由于错误被Actions.Copy组件内部处理,我们验证剪贴板调用即可 + expect(mockClipboard.writeText).toHaveBeenCalled(); + + consoleSpy.mockRestore(); }); }); @@ -349,6 +355,66 @@ describe('Mermaid Plugin', () => { }); }); + describe('onChange Event', () => { + it('should trigger onChange when switching to code view', () => { + const onChangeMock = jest.fn(); + render({mermaidContent}); + + const codeButton = screen.getByText('Code'); + fireEvent.click(codeButton); + + expect(onChangeMock).toHaveBeenCalledTimes(1); + expect(onChangeMock).toHaveBeenCalledWith('code'); + }); + + it('should trigger onChange when switching to image view', () => { + const onChangeMock = jest.fn(); + render({mermaidContent}); + + // 先切换到代码模式 + const codeButton = screen.getByText('Code'); + fireEvent.click(codeButton); + expect(onChangeMock).toHaveBeenCalledWith('code'); + + // 再切换回图片模式 + const imageButton = screen.getByText('Image'); + fireEvent.click(imageButton); + + expect(onChangeMock).toHaveBeenCalledTimes(2); + expect(onChangeMock).toHaveBeenCalledWith('image'); + }); + + it('should not trigger onChange when onChange prop is not provided', () => { + const { container } = render({mermaidContent}); + + const codeButton = screen.getByText('Code'); + fireEvent.click(codeButton); + + // 没有 onChange prop,不应该抛出错误 + expect(container.querySelector('.ant-mermaid')).toBeInTheDocument(); + }); + + it('should handle multiple mode switches with onChange', () => { + const onChangeMock = jest.fn(); + render({mermaidContent}); + + const codeButton = screen.getByText('Code'); + const imageButton = screen.getByText('Image'); + + // 多次切换 + fireEvent.click(codeButton); + fireEvent.click(imageButton); + fireEvent.click(codeButton); + fireEvent.click(imageButton); + + expect(onChangeMock).toHaveBeenCalledTimes(4); + expect(onChangeMock).toHaveBeenNthCalledWith(1, 'code'); + expect(onChangeMock).toHaveBeenNthCalledWith(2, 'image'); + expect(onChangeMock).toHaveBeenNthCalledWith(3, 'code'); + expect(onChangeMock).toHaveBeenNthCalledWith(4, 'image'); + }); + }); + describe('Error Handling', () => { it('should handle mermaid render errors', async () => { mockRender.mockRejectedValue(new Error('Render error')); @@ -396,6 +462,113 @@ describe('Mermaid Plugin', () => { expect(rtlContainer.querySelector('.ant-mermaid-rtl')).toBeInTheDocument(); }); + it('should apply transform styles to SVG element based on scale and position', async () => { + const { container } = render({mermaidContent}); + + await waitFor(() => { + expect(mockRender).toHaveBeenCalled(); + }); + + // 验证组件渲染后包含 SVG 元素 + const graphContainer = container.querySelector('.ant-mermaid-graph'); + expect(graphContainer).toBeInTheDocument(); + + // 验证缩放和拖动功能存在 + const zoomInButton = screen.getByLabelText('zoom-in'); + const zoomOutButton = screen.getByLabelText('zoom-out'); + const resetButton = screen.getByRole('button', { name: 'Reset' }); + + expect(zoomInButton).toBeInTheDocument(); + expect(zoomOutButton).toBeInTheDocument(); + expect(resetButton).toBeInTheDocument(); + }); + + it('should update transform styles when scale changes', async () => { + render({mermaidContent}); + + await waitFor(() => { + expect(mockRender).toHaveBeenCalled(); + }); + + // 验证缩放功能正常工作 + const zoomInButton = screen.getByLabelText('zoom-in'); + const zoomOutButton = screen.getByLabelText('zoom-out'); + + // 点击放大按钮应该触发缩放 + expect(() => fireEvent.click(zoomInButton)).not.toThrow(); + + // 点击缩小按钮应该触发缩放 + expect(() => fireEvent.click(zoomOutButton)).not.toThrow(); + }); + + it('should update transform styles when position changes during drag', async () => { + const { container } = render({mermaidContent}); + + await waitFor(() => { + expect(mockRender).toHaveBeenCalled(); + }); + + const graphContainer = container.querySelector('.ant-mermaid-graph') as HTMLElement; + + // 验证鼠标事件处理 + expect(() => { + fireEvent.mouseDown(graphContainer, { clientX: 100, clientY: 100 }); + fireEvent.mouseMove(graphContainer, { clientX: 150, clientY: 150 }); + fireEvent.mouseUp(graphContainer); + }).not.toThrow(); + }); + + it('should reset transform styles when reset is clicked', async () => { + render({mermaidContent}); + + await waitFor(() => { + expect(mockRender).toHaveBeenCalled(); + }); + + // 验证重置功能正常工作 + const resetButton = screen.getByRole('button', { name: 'Reset' }); + expect(() => fireEvent.click(resetButton)).not.toThrow(); + }); + + it('should not apply transform styles when in code view', async () => { + render({mermaidContent}); + + await waitFor(() => { + expect(mockRender).toHaveBeenCalled(); + }); + + // 切换到代码视图 + const codeButton = screen.getByText('Code'); + fireEvent.click(codeButton); + + // 在代码视图下,应该显示代码高亮器而不是 SVG + expect(screen.getByTestId('syntax-highlighter')).toBeInTheDocument(); + }); + + it('should handle edge cases for transform styles', async () => { + render({mermaidContent}); + + await waitFor(() => { + expect(mockRender).toHaveBeenCalled(); + }); + + const zoomOutButton = screen.getByLabelText('zoom-out'); + const zoomInButton = screen.getByLabelText('zoom-in'); + + // 验证边界值处理 + expect(() => { + // 多次点击缩小按钮测试最小值 + for (let i = 0; i < 10; i++) { + fireEvent.click(zoomOutButton); + } + + // 多次点击放大按钮测试最大值 + for (let i = 0; i < 20; i++) { + fireEvent.click(zoomInButton); + } + }).not.toThrow(); + }); + it('should apply custom styles through styles prop', () => { const customStyles = { root: { backgroundColor: 'red', padding: '10px' }, @@ -443,6 +616,60 @@ describe('Mermaid Plugin', () => { }); }); + describe('Transform Styles', () => { + it('should handle SVG transform styles correctly', async () => { + const { container } = render({mermaidContent}); + + await waitFor(() => { + expect(mockRender).toHaveBeenCalled(); + }); + + // 验证组件的基本功能 + const graphContainer = container.querySelector('.ant-mermaid-graph'); + expect(graphContainer).toBeInTheDocument(); + + // 验证交互元素存在 + expect(screen.getByLabelText('zoom-in')).toBeInTheDocument(); + expect(screen.getByLabelText('zoom-out')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Reset' })).toBeInTheDocument(); + }); + + it('should handle mouse events for transform updates', async () => { + const { container } = render({mermaidContent}); + + await waitFor(() => { + expect(mockRender).toHaveBeenCalled(); + }); + + const graphContainer = container.querySelector('.ant-mermaid-graph'); + expect(graphContainer).toBeInTheDocument(); + + // 验证鼠标事件不会导致错误 + expect(() => { + fireEvent.mouseDown(graphContainer!, { clientX: 100, clientY: 100 }); + fireEvent.mouseMove(graphContainer!, { clientX: 150, clientY: 150 }); + fireEvent.mouseUp(graphContainer!); + }).not.toThrow(); + }); + + it('should handle wheel events for zoom', async () => { + const { container } = render({mermaidContent}); + + await waitFor(() => { + expect(mockRender).toHaveBeenCalled(); + }); + + const graphContainer = container.querySelector('.ant-mermaid-graph'); + expect(graphContainer).toBeInTheDocument(); + + // 验证滚轮事件不会导致错误 + expect(() => { + fireEvent.wheel(graphContainer!, { deltaY: 100 }); + fireEvent.wheel(graphContainer!, { deltaY: -100 }); + }).not.toThrow(); + }); + }); + describe('Download Functionality', () => { it('should handle download correctly', async () => { // Mock DOM APIs diff --git a/packages/x/docs/x-markdown/demo/supersets/Mermaid/_semantic.tsx b/packages/x/components/mermaid/demo/_semantic.tsx similarity index 62% rename from packages/x/docs/x-markdown/demo/supersets/Mermaid/_semantic.tsx rename to packages/x/components/mermaid/demo/_semantic.tsx index de550f2ec..95a1a94a3 100644 --- a/packages/x/docs/x-markdown/demo/supersets/Mermaid/_semantic.tsx +++ b/packages/x/components/mermaid/demo/_semantic.tsx @@ -1,7 +1,7 @@ -import Mermaid from '@ant-design/x-markdown/plugins/Mermaid'; -import React from 'react'; -import SemanticPreview from '../../../../../.dumi/components/SemanticPreview'; -import useLocale from '../../../../../.dumi/hooks/useLocale'; +import { Mermaid } from '@ant-design/x'; +import React, { useState } from 'react'; +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; const locales = { cn: { @@ -18,8 +18,7 @@ const locales = { }, }; -const content = ` -graph TD +const content = `graph TD A[Start] --> B{Data Valid?} B -->|Yes| C[Process Data] B -->|No| D[Error Handling] @@ -31,6 +30,7 @@ graph TD const App: React.FC = () => { const [locale] = useLocale(locales); + const [renderType, setRenderType] = useState('image'); return ( { semantics={[ { name: 'root', desc: locale.root }, { name: 'header', desc: locale.header }, - { name: 'graph', desc: locale.graph }, - { name: 'code', desc: locale.code }, + renderType === 'image' + ? { name: 'graph', desc: locale.graph } + : { name: 'code', desc: locale.code }, ]} > - {content} + setRenderType(value)}>{content} ); }; diff --git a/packages/x/components/mermaid/demo/basic.tsx b/packages/x/components/mermaid/demo/basic.tsx new file mode 100644 index 000000000..25f02e7e0 --- /dev/null +++ b/packages/x/components/mermaid/demo/basic.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import Mermaid from '../Mermaid'; + +const App: React.FC = () => ( + + {`graph TD + A[开始] --> B{条件判断} + B -->|是| C[执行操作A] + B -->|否| D[执行操作B] + C --> E[结束] + D --> E`} + +); + +export default App; diff --git a/packages/x/components/mermaid/demo/custom-header.tsx b/packages/x/components/mermaid/demo/custom-header.tsx new file mode 100644 index 000000000..dd8263dd5 --- /dev/null +++ b/packages/x/components/mermaid/demo/custom-header.tsx @@ -0,0 +1,26 @@ +import { Button, Space } from 'antd'; +import React from 'react'; +import Mermaid from '../Mermaid'; + +const App: React.FC = () => { + const header = ( + + 流程图示例 + + + + ); + return ( + + {`flowchart LR + A[用户登录] --> B{验证身份} + B -->|成功| C[进入系统] + B -->|失败| D[显示错误] + C --> E[主界面] + D --> A`} + + ); +}; +export default App; diff --git a/packages/x/docs/x-markdown/demo/supersets/Mermaid/basic.tsx b/packages/x/components/mermaid/demo/with-xmarkdown.tsx similarity index 70% rename from packages/x/docs/x-markdown/demo/supersets/Mermaid/basic.tsx rename to packages/x/components/mermaid/demo/with-xmarkdown.tsx index c535c4181..2d59b6c4f 100644 --- a/packages/x/docs/x-markdown/demo/supersets/Mermaid/basic.tsx +++ b/packages/x/components/mermaid/demo/with-xmarkdown.tsx @@ -1,6 +1,5 @@ -import { Bubble } from '@ant-design/x'; +import { Bubble, Mermaid } from '@ant-design/x'; import XMarkdown, { type ComponentProps } from '@ant-design/x-markdown'; -import Mermaid from '@ant-design/x-markdown/plugins/Mermaid'; import { Button, Flex } from 'antd'; import React from 'react'; @@ -68,32 +67,41 @@ const Code: React.FC = (props) => { const App = () => { const [index, setIndex] = React.useState(0); - const timer = React.useRef(-1); + const timer = React.useRef(null); + const contentRef = React.useRef(null); + + React.useEffect(() => { + if (index >= text.length) return; - const renderStream = () => { - if (index >= text.length) { - clearTimeout(timer.current); - return; - } timer.current = setTimeout(() => { - setIndex((prev) => prev + 5); - renderStream(); + setIndex(Math.min(index + 5, text.length)); }, 20); - }; - React.useEffect(() => { - if (index === text.length) return; - renderStream(); return () => { - clearTimeout(timer.current); + if (timer.current) { + clearTimeout(timer.current); + timer.current = null; + } }; }, [index]); + React.useEffect(() => { + if (contentRef.current && index > 0 && index < text.length) { + const { scrollHeight, clientHeight } = contentRef.current; + if (scrollHeight > clientHeight) { + contentRef.current.scrollTo({ + top: scrollHeight, + behavior: 'smooth', + }); + } + } + }, [index]); + return ( - - + + + + +Basic +Custom Header +With XMarkdown + +## API + + +| Property | Description | Type | Default | +| --- | --- | --- | --- | +| children | Code content | `string` | - | +| header | Header | `React.ReactNode \| null` | React.ReactNode | +| className | Style class name | `string` | | +| classNames | Style class name | `string` | - | +| highlightProps | Code highlighting configuration | [`highlightProps`](https://github.com/react-syntax-highlighter/react-syntax-highlighter?tab=readme-ov-file#props) | - | + +## Semantic DOM + + + +## Theme Variables (Design Token) + + diff --git a/packages/x/components/mermaid/index.tsx b/packages/x/components/mermaid/index.tsx new file mode 100644 index 000000000..df9f5904f --- /dev/null +++ b/packages/x/components/mermaid/index.tsx @@ -0,0 +1,5 @@ +import Mermaid from './Mermaid'; + +export type { MermaidProps, MermaidType } from './Mermaid'; + +export default Mermaid; diff --git a/packages/x/components/mermaid/index.zh-CN.md b/packages/x/components/mermaid/index.zh-CN.md new file mode 100644 index 000000000..53a8faab4 --- /dev/null +++ b/packages/x/components/mermaid/index.zh-CN.md @@ -0,0 +1,43 @@ +--- +category: Components +group: + title: 反馈 + order: 4 +title: Mermaid +subtitle: 图表工具 +description: 用于渲染图表工具 Mermaid。 +cover: https://mdn.alipayobjects.com/huamei_lkxviz/afts/img/yTn9SILS900AAAAAPaAAAAgADtFMAQFr/original +coverDark: https://mdn.alipayobjects.com/huamei_lkxviz/afts/img/uYcMRYLDTCMAAAAAQBAAAAgADtFMAQFr/original +tag: 2.1.0 +--- + +## 何时使用 + +- 用于在应用中渲染支持缩放、平移、图像/代码双视图切换的交互式 Mermaid 图表。 +- 与 XMarkdown 结合使用,可在 Markdown 内容中渲染 Mermaid,并增强交互功能。 + +## 代码演示 + + +基本 +自定义 Header +配合 XMarkdown + +## API + + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| children | 代码内容 | `string` | - | +| header | 顶部 | `React.ReactNode \| null` | React.ReactNode | +| className | 样式类名 | `string` | | +| classNames | 样式类名 | `string` | - | +| highlightProps | 代码高亮配置 | [`highlightProps`](https://github.com/react-syntax-highlighter/react-syntax-highlighter?tab=readme-ov-file#props) | - | + +## Semantic DOM + + + +## 主题变量(Design Token) + + diff --git a/packages/x-markdown/src/plugins/Mermaid/style/index.ts b/packages/x/components/mermaid/style/index.ts similarity index 61% rename from packages/x-markdown/src/plugins/Mermaid/style/index.ts rename to packages/x/components/mermaid/style/index.ts index bfd33f6a9..1fee2ea38 100644 --- a/packages/x-markdown/src/plugins/Mermaid/style/index.ts +++ b/packages/x/components/mermaid/style/index.ts @@ -1,8 +1,33 @@ import type { CSSObject } from '@ant-design/cssinjs'; import { mergeToken } from '@ant-design/cssinjs-utils'; +import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/cssinjs-utils'; import { genStyleHooks } from '../../theme/genStyleUtils'; -import type { GenerateStyle } from '../../theme/interface'; -import type { FullToken, GetDefaultToken } from '../../theme/useToken'; + +export interface ComponentToken { + /** + * @desc 标题背景颜色 + * @descEN Title background color + */ + colorBgTitle: string; + + /** + * @desc 标题文本颜色 + * @descEN Title text color + */ + colorTextTitle: string; + + /** + * @desc 代码块边框颜色 + * @descEN Code block border color + */ + colorBorderCode: string; + + /** + * @desc 图表边框颜色 + * @descEN Graph border color + */ + colorBorderGraph: string; +} export interface MermaidToken extends FullToken<'Mermaid'> {} @@ -18,8 +43,8 @@ const genMermaidStyle: GenerateStyle = (token: MermaidToken): CSSO background: token.colorBgTitle, color: token.colorTextTitle, padding: token.paddingSM, - borderTopLeftRadius: token.borderRadius, - borderTopRightRadius: token.borderRadius, + borderStartStartRadius: token.borderRadius, + borderStartEndRadius: token.borderRadius, }, [`${componentCls}-graph`]: { display: 'flex', @@ -30,26 +55,30 @@ const genMermaidStyle: GenerateStyle = (token: MermaidToken): CSSO padding: token.paddingSM, background: token.colorBgContainer, overflow: 'auto', - borderBottomRightRadius: token.borderRadius, - borderBottomLeftRadius: token.borderRadius, + borderEndEndRadius: token.borderRadius, + borderEndStartRadius: token.borderRadius, + height: '400px', }, [`${componentCls}-graph-hidden`]: { display: 'none', }, [`${componentCls}-graph svg`]: { maxWidth: '100%', + maxHeight: '100%', height: 'auto', + width: 'auto', }, [`${componentCls}-code`]: { - borderBottomRightRadius: token.borderRadius, - borderBottomLeftRadius: token.borderRadius, + borderEndEndRadius: token.borderRadius, + borderEndStartRadius: token.borderRadius, borderBottom: `1px solid ${token.colorBgTitle}`, - borderLeft: `1px solid ${token.colorBgTitle}`, - borderRight: `1px solid ${token.colorBgTitle}`, + borderInlineStart: `1px solid ${token.colorBgTitle}`, + borderInlineEnd: `1px solid ${token.colorBgTitle}`, background: token.colorBgContainer, paddingInline: token.paddingSM, paddingBlock: token.paddingSM, - overflow: 'hidden', + overflow: 'auto', + height: '400px', 'pre,code': { whiteSpace: 'pre', fontSize: token.fontSize, @@ -69,14 +98,12 @@ const genMermaidStyle: GenerateStyle = (token: MermaidToken): CSSO }; }; -export const prepareComponentToken: GetDefaultToken<'Mermaid'> = (token) => { - return { - colorBgTitle: token.colorFillContent, - colorBorderCode: token.colorBorderSecondary, - colorBorderGraph: token.colorBorderSecondary, - colorTextTitle: token.colorText, - }; -}; +export const prepareComponentToken: GetDefaultToken<'Mermaid'> = (token) => ({ + colorBgTitle: token.colorFillContent, + colorBorderCode: token.colorBorderSecondary, + colorBorderGraph: token.colorBorderSecondary, + colorTextTitle: token.colorText, +}); export default genStyleHooks<'Mermaid'>( 'Mermaid', diff --git a/packages/x/components/notification/index.en-US.md b/packages/x/components/notification/index.en-US.md index 6f343fdf3..b84e1736e 100644 --- a/packages/x/components/notification/index.en-US.md +++ b/packages/x/components/notification/index.en-US.md @@ -7,8 +7,7 @@ title: Notification description: Send system-level notifications that are displayed outside the page. cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*cRmqTY4nKPEAAAAAAAAAAAAADrJ8AQ/original coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*W3RmSov-xVMAAAAAAAAAAAAADrJ8AQ/original -demo: - cols: 1 +tag: 2.0.0 --- ## When to Use diff --git a/packages/x/components/notification/index.zh-CN.md b/packages/x/components/notification/index.zh-CN.md index 19d4ca3fc..cf4c812f2 100644 --- a/packages/x/components/notification/index.zh-CN.md +++ b/packages/x/components/notification/index.zh-CN.md @@ -8,8 +8,7 @@ subtitle: 系统通知 description: 系统级别发送在页面外部显示的通知。 cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*cRmqTY4nKPEAAAAAAAAAAAAADrJ8AQ/original coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*W3RmSov-xVMAAAAAAAAAAAAADrJ8AQ/original -demo: - cols: 1 +tag: 2.0.0 --- ## 何时使用 diff --git a/packages/x/components/prompts/index.en-US.md b/packages/x/components/prompts/index.en-US.md index 6c442795b..d440b32f4 100644 --- a/packages/x/components/prompts/index.en-US.md +++ b/packages/x/components/prompts/index.en-US.md @@ -8,8 +8,6 @@ order: 1 description: Display a predefined set of questions or suggestion. cover: https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*UfhXRamlAf0AAAAAAAAAAAAADgCCAQ/original coverDark: https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*3CN5RLKP0X4AAAAAAAAAAAAADgCCAQ/original -demo: - cols: 1 --- ## When To Use @@ -45,12 +43,6 @@ The Prompts component is used to display a predefined set of questions or sugges | fadeIn | Fade in effect | boolean | - | - | | fadeInLeft | Fade left in effect | boolean | - | - | -#### SemanticType - -```typescript | pure -type SemanticType = 'list' | 'item' | 'content' | 'title' | 'subList' | 'subItem'; -``` - ### PromptProps | Property | Description | Type | Default | Version | diff --git a/packages/x/components/prompts/index.tsx b/packages/x/components/prompts/index.tsx index ff3e1f547..d9b032b6f 100644 --- a/packages/x/components/prompts/index.tsx +++ b/packages/x/components/prompts/index.tsx @@ -1,7 +1,7 @@ +import CSSMotion from '@rc-component/motion'; +import { composeRef } from '@rc-component/util/lib/ref'; import { Typography } from 'antd'; -import classnames from 'classnames'; -import CSSMotion from 'rc-motion'; -import { composeRef } from 'rc-util/lib/ref'; +import { clsx } from 'clsx'; import React from 'react'; import useProxyImperativeHandle from '../_util/hooks/use-proxy-imperative-handle'; import useXComponentConfig from '../_util/hooks/use-x-component-config'; @@ -153,7 +153,7 @@ const ForwardPrompts = React.forwardRef((props, ref) = // ============================ Style ============================ const [hashId, cssVarCls] = useStyle(prefixCls); - const mergedCls = classnames( + const mergedCls = clsx( prefixCls, contextConfig.className, className, @@ -166,7 +166,7 @@ const ForwardPrompts = React.forwardRef((props, ref) = }, ); - const mergedListCls = classnames( + const mergedListCls = clsx( `${prefixCls}-list`, contextConfig.classNames.list, classNames.list, @@ -196,14 +196,14 @@ const ForwardPrompts = React.forwardRef((props, ref) =
      {/* Title */} {title && ( ((props, ref) =
      ((props, ref) = {info.icon &&
      {info.icon}
      } {/* Content */}
      { return (
      ((props { value: customValue, onChange: (key) => { - onChange?.(key || false); + onChange?.(key as boolean); }, }, ); @@ -97,7 +97,7 @@ const SenderSwitch = React.forwardRef((props // ============================ style ============================ const contextConfig = useXComponentConfig('sender'); - const mergedCls = classnames( + const mergedCls = clsx( prefixCls, switchCls, className, @@ -129,7 +129,7 @@ const SenderSwitch = React.forwardRef((props
      ); diff --git a/packages/x/components/sender/TextArea.tsx b/packages/x/components/sender/TextArea.tsx index 3cb57461e..9a6c56a03 100644 --- a/packages/x/components/sender/TextArea.tsx +++ b/packages/x/components/sender/TextArea.tsx @@ -1,11 +1,11 @@ +import pickAttrs from '@rc-component/util/lib/pickAttrs'; +import getValue from '@rc-component/util/lib/utils/get'; import type { InputRef as AntdInputRef, InputRef } from 'antd'; import { Input } from 'antd'; -import classnames from 'classnames'; -import pickAttrs from 'rc-util/lib/pickAttrs'; -import getValue from 'rc-util/lib/utils/get'; +import { clsx } from 'clsx'; import React from 'react'; import { SenderContext } from './context'; -import { insertPosition } from './interface'; +import type { InsertPosition, SkillType } from './interface'; function getComponent( components: { input?: React.ComponentType } | undefined, @@ -19,9 +19,9 @@ export interface TextAreaRef { nativeElement: InputRef['nativeElement']; focus: InputRef['focus']; blur: InputRef['blur']; - insert: (value: string, position?: insertPosition) => void; + insert: (value: string, position?: InsertPosition) => void; clear: () => void; - getValue: () => { value: string; config: any[] }; + getValue: () => { value: string; slotConfig: any[]; skill?: SkillType }; } const TextArea = React.forwardRef((_, ref) => { @@ -40,7 +40,8 @@ const TextArea = React.forwardRef((_, ref) => { classNames = {}, autoSize, components, - onSubmit, + submitDisabled, + triggerSend, placeholder, onFocus, onBlur, @@ -85,7 +86,7 @@ const TextArea = React.forwardRef((_, ref) => { }; const getValue = () => { - return { value: value || '', config: [] }; + return { value: value || '', slotConfig: [] }; }; React.useImperativeHandle(ref, () => { @@ -111,29 +112,28 @@ const TextArea = React.forwardRef((_, ref) => { }; const onInternalKeyDown: React.KeyboardEventHandler = (e) => { - const canSubmit = e.key === 'Enter'; - if (isCompositionRef.current || !canSubmit) { - onKeyDown?.(e as unknown as React.KeyboardEvent); + const eventRes = onKeyDown?.(e); + const { key, shiftKey, ctrlKey, altKey, metaKey } = e; + + if (isCompositionRef.current || key !== 'Enter' || eventRes === false) { return; } - switch (submitType) { - case 'enter': - if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) { - e.preventDefault(); - onSubmit?.(value || ''); - } - break; - - case 'shiftEnter': - if (e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) { - e.preventDefault(); - onSubmit?.(value || ''); + // 处理Enter键提交 + if (key === 'Enter') { + const isModifierPressed = ctrlKey || altKey || metaKey; + const shouldSubmit = + (submitType === 'enter' && !shiftKey && !isModifierPressed) || + (submitType === 'shiftEnter' && shiftKey && !isModifierPressed); + + if (shouldSubmit) { + e.preventDefault(); + if (!submitDisabled) { + triggerSend?.(); } - break; + return; + } } - - onKeyDown?.(e); }; // ============================ Paste ============================= @@ -174,7 +174,7 @@ const TextArea = React.forwardRef((_, ref) => { {...inputProps} disabled={disabled} style={styles.input} - className={classnames(`${prefixCls}-input`, classNames.input)} + className={clsx(`${prefixCls}-input`, classNames.input)} autoSize={autoSize} value={value} onChange={mergeOnChange} diff --git a/packages/x/components/sender/__tests__/__snapshots__/demo-extend.test.ts.snap b/packages/x/components/sender/__tests__/__snapshots__/demo-extend.test.ts.snap index c14bac98c..8b56b622a 100644 --- a/packages/x/components/sender/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/packages/x/components/sender/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -2,189 +2,90 @@ exports[`renders components/sender/demo/agent.tsx extend context correctly 1`] = `
      -
      -
      - Please help me search for news about + + Please write an article about