diff --git a/package.json b/package.json index 961edfc..5aef6c1 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,8 @@ "react-layout-kit": "^1", "shiki": "^1.10.3", "swr": "^2.2.5", - "url-join": "^5.0.0" + "url-join": "^5.0.0", + "use-merge-value": "^1.2.0" }, "devDependencies": { "@testing-library/react": "^14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac768bb..3f773af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: url-join: specifier: ^5.0.0 version: 5.0.0 + use-merge-value: + specifier: ^1.2.0 + version: 1.2.0(react@18.3.1) devDependencies: '@testing-library/react': specifier: ^14 @@ -95,7 +98,7 @@ importers: version: 2.0.4(vitest@2.0.4(@types/node@22.0.0)(jsdom@22.1.0)(less@4.1.3)(lightningcss@1.22.1)(sass@1.77.8)(terser@5.31.3)) '@yuntijs/lint': specifier: ^1.7.0 - version: 1.7.1(@octokit/core@5.2.0)(encoding@0.1.13)(eslint@8.57.0)(jest@27.5.1)(postcss@8.4.40)(prettier@3.3.3)(semantic-release@21.1.2(typescript@5.5.4))(stylelint@15.11.0(typescript@5.5.4))(typescript@5.5.4) + version: 1.7.1(@octokit/core@4.2.4(encoding@0.1.13))(encoding@0.1.13)(eslint@8.57.0)(jest@27.5.1)(postcss@8.4.40)(prettier@3.3.3)(semantic-release@21.1.2(typescript@5.5.4))(stylelint@15.11.0(typescript@5.5.4))(typescript@5.5.4) antd: specifier: ^5.19.3 version: 5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -113,16 +116,16 @@ importers: version: 1.11.12 dumi: specifier: ^2.4.7 - version: 2.4.7(@babel/core@7.25.2)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0) + version: 2.4.7(@babel/core@7.23.6)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0) dumi-theme-yunti: specifier: ^1.1.7 - version: 1.1.7(@giscus/react@3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@lobehub/ui@1.147.0(@types/react-dom@18.3.0)(@types/react@18.2.40)(ahooks@3.8.0(react@18.3.1))(antd-style@3.6.2(@types/react@18.2.40)(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(lucide-react@0.417.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.2.40)(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dumi@2.4.7(@babel/core@7.25.2)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0))(immer@10.1.1)(lucide-react@0.417.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-layout-kit@1.9.0(react@18.3.1))(react@18.3.1) + version: 1.1.7(@giscus/react@3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@lobehub/ui@1.147.0(@types/react-dom@18.3.0)(@types/react@18.2.40)(ahooks@3.8.0(react@18.3.1))(antd-style@3.6.2(@types/react@18.2.40)(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(lucide-react@0.417.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.2.40)(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dumi@2.4.7(@babel/core@7.23.6)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0))(immer@10.1.1)(lucide-react@0.417.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-layout-kit@1.9.0(react@18.3.1))(react@18.3.1) eslint: specifier: ^8.56.0 version: 8.57.0 father: specifier: ^4.3.8 - version: 4.4.5(@babel/core@7.25.2)(@types/node@22.0.0)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(type-fest@4.23.0)(webpack@5.93.0) + version: 4.4.5(@babel/core@7.23.6)(@types/node@22.0.0)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(type-fest@4.23.0)(webpack@5.93.0) husky: specifier: ^8 version: 8.0.3 @@ -11077,10 +11080,10 @@ packages: snapshots: - '@achingbrain/semantic-release-github@0.0.2(@octokit/core@5.2.0)(encoding@0.1.13)(semantic-release@21.1.2(typescript@5.5.4))': + '@achingbrain/semantic-release-github@0.0.2(@octokit/core@4.2.4(encoding@0.1.13))(encoding@0.1.13)(semantic-release@21.1.2(typescript@5.5.4))': dependencies: '@octokit/plugin-retry': 3.0.9 - '@octokit/plugin-throttling': 3.7.0(@octokit/core@5.2.0) + '@octokit/plugin-throttling': 3.7.0(@octokit/core@4.2.4(encoding@0.1.13)) '@octokit/rest': 19.0.13(encoding@0.1.13) '@semantic-release/error': 3.0.0 aggregate-error: 3.1.0 @@ -11342,34 +11345,64 @@ snapshots: dependencies: '@babel/types': 7.25.2 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.23.6)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.25.2)': @@ -11377,31 +11410,61 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.6)': + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -11412,10 +11475,10 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.25.2)': + '@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.6)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/core': 7.23.6 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.23.6) '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-simple-access': 7.24.7 transitivePeerDependencies: @@ -12894,9 +12957,9 @@ snapshots: '@octokit/types': 12.6.0 bottleneck: 2.19.5 - '@octokit/plugin-throttling@3.7.0(@octokit/core@5.2.0)': + '@octokit/plugin-throttling@3.7.0(@octokit/core@4.2.4(encoding@0.1.13))': dependencies: - '@octokit/core': 5.2.0 + '@octokit/core': 4.2.4(encoding@0.1.13) '@octokit/types': 6.41.0 bottleneck: 2.19.5 @@ -14427,13 +14490,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@umijs/test@4.3.10(@babel/core@7.25.2)': + '@umijs/test@4.3.10(@babel/core@7.23.6)': dependencies: - '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.25.2) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.6) '@jest/types': 27.5.1 '@umijs/bundler-utils': 4.3.10 '@umijs/utils': 4.3.10 - babel-jest: 29.7.0(@babel/core@7.25.2) + babel-jest: 29.7.0(@babel/core@7.23.6) esbuild: 0.21.4 identity-obj-proxy: 3.0.0 isomorphic-unfetch: 4.0.2 @@ -14602,7 +14665,7 @@ snapshots: '@xtuc/long@4.2.2': {} - '@yuntijs/lint@1.7.1(@octokit/core@5.2.0)(encoding@0.1.13)(eslint@8.57.0)(jest@27.5.1)(postcss@8.4.40)(prettier@3.3.3)(semantic-release@21.1.2(typescript@5.5.4))(stylelint@15.11.0(typescript@5.5.4))(typescript@5.5.4)': + '@yuntijs/lint@1.7.1(@octokit/core@4.2.4(encoding@0.1.13))(encoding@0.1.13)(eslint@8.57.0)(jest@27.5.1)(postcss@8.4.40)(prettier@3.3.3)(semantic-release@21.1.2(typescript@5.5.4))(stylelint@15.11.0(typescript@5.5.4))(typescript@5.5.4)': dependencies: '@commitlint/config-conventional': 18.6.3 '@trivago/prettier-plugin-sort-imports': 4.3.0(prettier@3.3.3) @@ -14661,7 +14724,7 @@ snapshots: remark-sort-definitions: 1.0.5 remark-textr: 5.0.1 remark-toc: 9.0.0 - semantic-release-config-gitmoji: 1.5.3(@octokit/core@5.2.0)(encoding@0.1.13)(semantic-release@21.1.2(typescript@5.5.4)) + semantic-release-config-gitmoji: 1.5.3(@octokit/core@4.2.4(encoding@0.1.13))(encoding@0.1.13)(semantic-release@21.1.2(typescript@5.5.4)) stylelint-config-clean-order: 5.4.2(stylelint@15.11.0(typescript@5.5.4)) stylelint-config-recommended: 13.0.0(stylelint@15.11.0(typescript@5.5.4)) stylelint-less: 2.0.0(postcss@8.4.40)(stylelint@15.11.0(typescript@5.5.4)) @@ -15090,13 +15153,13 @@ snapshots: transitivePeerDependencies: - supports-color - babel-jest@29.7.0(@babel/core@7.25.2): + babel-jest@29.7.0(@babel/core@7.23.6): dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.23.6 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.25.2) + babel-preset-jest: 29.6.3(@babel/core@7.23.6) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -15166,11 +15229,11 @@ snapshots: zod: 3.23.8 zod-validation-error: 2.1.0(zod@3.23.8) - babel-plugin-styled-components@2.1.4(@babel/core@7.25.2)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): + babel-plugin-styled-components@2.1.4(@babel/core@7.23.6)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): dependencies: '@babel/helper-annotate-as-pure': 7.24.7 '@babel/helper-module-imports': 7.24.7 - '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.23.6) lodash: 4.17.21 picomatch: 2.3.1 styled-components: 6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -15183,6 +15246,22 @@ snapshots: lodash: 4.17.21 traverse: 0.6.6 + babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.6): + dependencies: + '@babel/core': 7.23.6 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.6) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.6) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.6) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.6) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.6) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.6) + babel-preset-current-node-syntax@1.0.1(@babel/core@7.25.2): dependencies: '@babel/core': 7.25.2 @@ -15205,11 +15284,11 @@ snapshots: babel-plugin-jest-hoist: 27.5.1 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.25.2) - babel-preset-jest@29.6.3(@babel/core@7.25.2): + babel-preset-jest@29.6.3(@babel/core@7.23.6): dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.23.6 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.25.2) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.6) bail@2.0.2: {} @@ -16337,7 +16416,7 @@ snapshots: dumi-assets-types@2.3.0: {} - dumi-theme-yunti@1.1.7(@giscus/react@3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@lobehub/ui@1.147.0(@types/react-dom@18.3.0)(@types/react@18.2.40)(ahooks@3.8.0(react@18.3.1))(antd-style@3.6.2(@types/react@18.2.40)(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(lucide-react@0.417.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.2.40)(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dumi@2.4.7(@babel/core@7.25.2)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0))(immer@10.1.1)(lucide-react@0.417.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-layout-kit@1.9.0(react@18.3.1))(react@18.3.1): + dumi-theme-yunti@1.1.7(@giscus/react@3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@lobehub/ui@1.147.0(@types/react-dom@18.3.0)(@types/react@18.2.40)(ahooks@3.8.0(react@18.3.1))(antd-style@3.6.2(@types/react@18.2.40)(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(lucide-react@0.417.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.2.40)(antd@5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dumi@2.4.7(@babel/core@7.23.6)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0))(immer@10.1.1)(lucide-react@0.417.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-layout-kit@1.9.0(react@18.3.1))(react@18.3.1): dependencies: '@ant-design/cssinjs': 1.21.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@emotion/css': 11.13.0 @@ -16348,7 +16427,7 @@ snapshots: ahooks: 3.8.0(react@18.3.1) antd: 5.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) chalk: 4.1.2 - dumi: 2.4.7(@babel/core@7.25.2)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0) + dumi: 2.4.7(@babel/core@7.23.6)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0) fast-deep-equal: 3.1.3 history: 5.3.0 lodash-es: 4.17.21 @@ -16365,7 +16444,7 @@ snapshots: - immer - supports-color - dumi@2.4.7(@babel/core@7.25.2)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0): + dumi@2.4.7(@babel/core@7.23.6)(@swc/helpers@0.5.1)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0): dependencies: '@ant-design/icons-svg': 4.4.2 '@makotot/ghostui': 2.0.0(react@18.3.1) @@ -16430,7 +16509,7 @@ snapshots: sass: 1.77.8 sitemap: 7.1.2 sucrase: 3.35.0 - umi: 4.3.10(@babel/core@7.25.2)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(sass@1.77.8)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0) + umi: 4.3.10(@babel/core@7.23.6)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(sass@1.77.8)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0) unified: 10.1.2 unist-util-visit: 4.1.2 unist-util-visit-parents: 5.1.3 @@ -17244,7 +17323,7 @@ snapshots: dependencies: reusify: 1.0.4 - father@4.4.5(@babel/core@7.25.2)(@types/node@22.0.0)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(type-fest@4.23.0)(webpack@5.93.0): + father@4.4.5(@babel/core@7.23.6)(@types/node@22.0.0)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(type-fest@4.23.0)(webpack@5.93.0): dependencies: '@microsoft/api-extractor': 7.39.1(@types/node@22.0.0) '@umijs/babel-preset-umi': 4.3.10 @@ -17256,7 +17335,7 @@ snapshots: '@vercel/ncc': 0.33.3 babel-plugin-dynamic-import-node: 2.3.3 babel-plugin-module-resolver: 4.1.0 - babel-plugin-styled-components: 2.1.4(@babel/core@7.25.2)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + babel-plugin-styled-components: 2.1.4(@babel/core@7.23.6)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) babel-plugin-transform-define: 2.0.1 enhanced-resolve: 5.9.3 esbuild: 0.17.19 @@ -23248,12 +23327,12 @@ snapshots: select-hose@2.0.0: {} - semantic-release-config-gitmoji@1.5.3(@octokit/core@5.2.0)(encoding@0.1.13)(semantic-release@21.1.2(typescript@5.5.4)): + semantic-release-config-gitmoji@1.5.3(@octokit/core@4.2.4(encoding@0.1.13))(encoding@0.1.13)(semantic-release@21.1.2(typescript@5.5.4)): dependencies: '@gitmoji/commit-types': 1.1.5 '@semantic-release/changelog': 6.0.3(semantic-release@21.1.2(typescript@5.5.4)) '@semantic-release/git': 10.0.1(semantic-release@21.1.2(typescript@5.5.4)) - '@semantic-release/github': '@achingbrain/semantic-release-github@0.0.2(@octokit/core@5.2.0)(encoding@0.1.13)(semantic-release@21.1.2(typescript@5.5.4))' + '@semantic-release/github': '@achingbrain/semantic-release-github@0.0.2(@octokit/core@4.2.4(encoding@0.1.13))(encoding@0.1.13)(semantic-release@21.1.2(typescript@5.5.4))' '@semrel-extra/npm': 1.2.2 '@types/semantic-release': 17.2.11 conventional-changelog-gitmoji-config: 1.5.2(encoding@0.1.13) @@ -24290,7 +24369,7 @@ snapshots: uglify-js@3.19.1: optional: true - umi@4.3.10(@babel/core@7.25.2)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(sass@1.77.8)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0): + umi@4.3.10(@babel/core@7.23.6)(@types/node@22.0.0)(@types/react@18.2.40)(eslint@8.57.0)(jest@27.5.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(sass@1.77.8)(stylelint@15.11.0(typescript@5.5.4))(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0): dependencies: '@babel/runtime': 7.23.6 '@umijs/bundler-utils': 4.3.10 @@ -24300,7 +24379,7 @@ snapshots: '@umijs/preset-umi': 4.3.10(@types/node@22.0.0)(@types/react@18.2.40)(lightningcss@1.22.1)(rollup@3.29.4)(sass@1.77.8)(terser@5.31.3)(type-fest@4.23.0)(typescript@5.5.4)(webpack@5.93.0) '@umijs/renderer-react': 4.3.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@umijs/server': 4.3.10 - '@umijs/test': 4.3.10(@babel/core@7.25.2) + '@umijs/test': 4.3.10(@babel/core@7.23.6) '@umijs/utils': 4.3.10 prettier-plugin-organize-imports: 3.2.4(prettier@3.3.3)(typescript@5.5.4) prettier-plugin-packagejson: 2.4.3(prettier@3.3.3) diff --git a/src/ChatItem/components/MessageContent.tsx b/src/ChatItem/components/MessageContent.tsx new file mode 100644 index 0000000..2300847 --- /dev/null +++ b/src/ChatItem/components/MessageContent.tsx @@ -0,0 +1,79 @@ +import { MarkdownProps } from '@lobehub/ui'; +import { useResponsive } from 'antd-style'; +import { type ReactNode, memo } from 'react'; +import { Flexbox } from 'react-layout-kit'; + +import EditableMessage from '@/EditableMessage'; + +import { useStyles } from '../style'; +import { ChatItemProps } from '../type'; + +export interface MessageContentProps { + editing?: ChatItemProps['editing']; + fontSize?: number; + message?: ReactNode; + messageExtra?: ChatItemProps['messageExtra']; + onChange?: ChatItemProps['onChange']; + onDoubleClick?: ChatItemProps['onDoubleClick']; + onEditingChange?: ChatItemProps['onEditingChange']; + placement?: ChatItemProps['placement']; + primary?: ChatItemProps['primary']; + renderMessage?: ChatItemProps['renderMessage']; + text?: ChatItemProps['text']; + type?: ChatItemProps['type']; + markdownProps?: MarkdownProps; + markdownClassname?: string; +} + +const MessageContent = memo( + ({ + editing, + onChange, + onEditingChange, + text, + message, + placement, + messageExtra, + renderMessage, + type, + primary, + onDoubleClick, + fontSize, + markdownProps, + markdownClassname, + }) => { + const { cx, styles } = useStyles({ editing, placement, primary, type }); + const { mobile } = useResponsive(); + + const content = ( + + ); + const messageContent = renderMessage ? renderMessage(content) : content; + + return ( + + {messageContent} + {messageExtra && !editing ? ( +
{messageExtra}
+ ) : null} +
+ ); + } +); + +export default MessageContent; diff --git a/src/ChatItem/demos/Alert.tsx b/src/ChatItem/demos/Alert.tsx new file mode 100644 index 0000000..745415b --- /dev/null +++ b/src/ChatItem/demos/Alert.tsx @@ -0,0 +1,47 @@ +import { Highlighter, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; + +import ChatItem, { ChatItemProps } from '..'; +import { avatar } from './data'; + +const demoError = { + details: { + exception: + 'Validation filter failedId-f5aab7304f6c754804f70000Id-f5aab7304f6c754804f70000Id-f5aab7304f6c754804f70000Id-f5aab7304f6c754804f70000Id-f5aab7304f6c754804f70000Id-f5aab7304f6c754804f70000', + msgId: + 'Id-f5aab7304f6c754804f70000Id-f5aab7304f6c754804f70000Id-f5aab7304f6c754804f70000Id-f5aab7304f6c754804f70000Id-f5aab7304f6c754804f70000', + }, + reasons: [ + { + language: 'en', + message: 'Validation filter failed', + }, + ], +}; +export default () => { + const store = useCreateStore(); + const control: ChatItemProps['error'] | any = useControls( + { + description: '', + message: 'Error', + type: { + options: ['success', 'info', 'warning', 'error'], + value: 'error', + }, + }, + { store } + ); + + return ( + + + {JSON.stringify(demoError, null, 2)} + + } + /> + + ); +}; diff --git a/src/ChatItem/demos/Tmarkdown.tsx b/src/ChatItem/demos/Tmarkdown.tsx new file mode 100644 index 0000000..a15999a --- /dev/null +++ b/src/ChatItem/demos/Tmarkdown.tsx @@ -0,0 +1,61 @@ +import { ActionIconGroup, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; +import { useState } from 'react'; + +import ChatItem, { ChatItemProps } from '..'; +import { content } from '../../EditableMessage/demos/data'; +import { avatar, dropdownMenu, items } from './data'; + +export default () => { + const [edit, setEdit] = useState(false); + const store = useCreateStore(); + const control: ChatItemProps | any = useControls( + { + loading: false, + message: { + rows: true, + value: content, + }, + placement: { + options: ['left', 'right'], + value: 'left', + }, + primary: false, + showTitle: false, + time: 1_686_538_950_084, + type: { + options: ['block', 'pure'], + value: 'block', + }, + markdownProps: { + fontSize: 15, + lineHeight: 1.625, + headerMultiple: 0, + }, + markdownClassname: 'customMarkdown', + }, + { store } + ); + + return ( + + { + if (action.key === 'edit') { + setEdit(true); + } + }} + type="ghost" + /> + } + avatar={avatar} + editing={edit} + onEditingChange={setEdit} + /> + + ); +}; diff --git a/src/ChatItem/demos/data.tsx b/src/ChatItem/demos/data.tsx new file mode 100644 index 0000000..5ccc144 --- /dev/null +++ b/src/ChatItem/demos/data.tsx @@ -0,0 +1,37 @@ +import { type ActionIconGroupProps, MetaData } from '@lobehub/ui'; +import { Copy, Edit, RotateCw, Trash } from 'lucide-react'; + +export const avatar: MetaData = { + avatar: '😎', + backgroundColor: '#E8DA5A', + title: 'Advertiser', +}; + +export const items: ActionIconGroupProps['items'] = [ + { + icon: Edit, + key: 'edit', + label: 'Edit', + }, +]; + +export const dropdownMenu: ActionIconGroupProps['dropdownMenu'] = [ + { + icon: Copy, + key: 'copy', + label: 'Copy', + }, + { + icon: RotateCw, + key: 'regenerate', + label: 'Regenerate', + }, + { + type: 'divider', + }, + { + icon: Trash, + key: 'delete', + label: 'Delete', + }, +]; diff --git a/src/ChatItem/demos/index.tsx b/src/ChatItem/demos/index.tsx new file mode 100644 index 0000000..8c460b0 --- /dev/null +++ b/src/ChatItem/demos/index.tsx @@ -0,0 +1,55 @@ +import { ActionIconGroup, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; +import { useState } from 'react'; + +import ChatItem, { ChatItemProps } from '..'; +import { avatar, dropdownMenu, items } from './data'; + +export default () => { + const [edit, setEdit] = useState(false); + const store = useCreateStore(); + const control: ChatItemProps | any = useControls( + { + loading: false, + message: { + rows: true, + value: + "要使用 dayjs 的 fromNow 函数,需要先安装 dayjs 库并在代码中引入它。然后,可以使用以下语法来获取当前时间与给定时间之间的相对时间:\n\n```javascript\ndayjs().fromNow();\ndayjs('2021-05-01').fromNow();\n```", + }, + placement: { + options: ['left', 'right'], + value: 'left', + }, + primary: false, + showTitle: false, + time: 1_686_538_950_084, + type: { + options: ['block', 'pure'], + value: 'block', + }, + }, + { store } + ); + + return ( + + { + if (action.key === 'edit') { + setEdit(true); + } + }} + type="ghost" + /> + } + avatar={avatar} + editing={edit} + onEditingChange={setEdit} + /> + + ); +}; diff --git a/src/ChatItem/index.md b/src/ChatItem/index.md new file mode 100644 index 0000000..d0fe7ee --- /dev/null +++ b/src/ChatItem/index.md @@ -0,0 +1,22 @@ +--- +nav: Components +group: Chat +title: ChatItem +description: ChatItem is a React component that represents a single item in a chat conversation. It displays the user's avatar, name, and message. It can also display a loading indicator if the message is still being sent. +--- + +## Default + + + +## Markdown + + + +## Alert + + + +## APIs + + diff --git a/src/ChatItem/index.tsx b/src/ChatItem/index.tsx new file mode 100644 index 0000000..f673928 --- /dev/null +++ b/src/ChatItem/index.tsx @@ -0,0 +1,123 @@ +'use client'; + +import Actions from '@lobehub/ui/es/ChatItem/components/Actions'; +import Avatar from '@lobehub/ui/es/ChatItem/components/Avatar'; +import BorderSpacing from '@lobehub/ui/es/ChatItem/components/BorderSpacing'; +import ErrorContent from '@lobehub/ui/es/ChatItem/components/ErrorContent'; +import Title from '@lobehub/ui/es/ChatItem/components/Title'; +import { useStyles } from '@lobehub/ui/es/ChatItem/style'; +import { useResponsive } from 'antd-style'; +import { memo } from 'react'; +import { Flexbox } from 'react-layout-kit'; + +import MessageContent from './components/MessageContent'; +import { ChatItemProps } from './type'; + +const MOBILE_AVATAR_SIZE = 32; + +const ChatItem = memo( + ({ + avatarAddon, + onAvatarClick, + avatarProps, + actions, + className, + primary, + loading, + message, + placement = 'left', + type = 'block', + avatar, + error, + showTitle, + time, + editing, + onChange, + onEditingChange, + messageExtra, + renderMessage, + text, + errorMessage, + onDoubleClick, + fontSize, + markdownProps, + markdownClassname, + ...rest + }) => { + const { mobile } = useResponsive(); + const { cx, styles } = useStyles({ + editing, + placement, + primary, + showTitle, + time, + title: avatar.title, + type, + }); + + return ( + + + + + <Flexbox + align={placement === 'left' ? 'flex-start' : 'flex-end'} + className={styles.messageContent} + direction={ + // eslint-disable-next-line no-nested-ternary + type === 'block' + ? placement === 'left' + ? 'horizontal' + : 'horizontal-reverse' + : 'vertical' + } + gap={8} + > + {error ? ( + <ErrorContent error={error} message={errorMessage} placement={placement} /> + ) : ( + <MessageContent + editing={editing} + fontSize={fontSize} + markdownClassname={markdownClassname} + markdownProps={markdownProps} + message={message} + messageExtra={messageExtra} + onChange={onChange} + onDoubleClick={onDoubleClick} + onEditingChange={onEditingChange} + placement={placement} + primary={primary} + renderMessage={renderMessage} + text={text} + type={type} + /> + )} + <Actions actions={actions} editing={editing} placement={placement} type={type} /> + </Flexbox> + </Flexbox> + {mobile && type === 'block' && <BorderSpacing borderSpacing={MOBILE_AVATAR_SIZE} />} + </Flexbox> + ); + } +); + +export default ChatItem; + +export type { ChatItemProps } from './type'; diff --git a/src/ChatItem/style.ts b/src/ChatItem/style.ts new file mode 100644 index 0000000..e1ba478 --- /dev/null +++ b/src/ChatItem/style.ts @@ -0,0 +1,214 @@ +/* eslint-disable no-nested-ternary */ +import { createStyles } from 'antd-style'; + +export const useStyles = createStyles( + ( + { cx, css, token, isDarkMode, responsive }, + { + placement, + type, + title, + primary, + avatarSize, + editing, + time, + }: { + avatarSize?: number; + editing?: boolean; + placement?: 'left' | 'right'; + primary?: boolean; + showTitle?: boolean; + time?: number; + title?: string; + type?: 'block' | 'pure'; + } + ) => { + const blockStylish = css` + padding-block: 8px; + padding-inline: 12px; + + background-color: ${primary + ? isDarkMode + ? token.colorFill + : token.colorBgElevated + : isDarkMode + ? token.colorFillSecondary + : token.colorBgContainer}; + border-radius: ${token.borderRadiusLG}px; + + transition: background-color 100ms ${token.motionEaseOut}; + `; + + const pureStylish = css` + padding-block-start: ${title ? 0 : '6px'}; + `; + + const pureContainerStylish = css` + margin-block-end: -16px; + transition: background-color 100ms ${token.motionEaseOut}; + `; + + const typeStylish = type === 'block' ? blockStylish : pureStylish; + + const editingStylish = + editing && + css` + width: 100%; + `; + + return { + actions: cx( + css` + flex: none; + align-self: ${type === 'block' + ? 'flex-end' + : placement === 'left' + ? 'flex-start' + : 'flex-end'}; + justify-content: ${placement === 'left' ? 'flex-end' : 'flex-start'}; + `, + editing && + css` + pointer-events: none !important; + opacity: 0 !important; + ` + ), + avatarContainer: css` + position: relative; + flex: none; + width: ${avatarSize}px; + height: ${avatarSize}px; + `, + avatarGroupContainer: css` + width: ${avatarSize}px; + `, + container: cx( + type === 'pure' && pureContainerStylish, + css` + position: relative; + width: 100%; + max-width: 100vw; + padding: 16px; + + time { + display: inline-block; + white-space: nowrap; + } + + div[role='menubar'] { + display: flex; + } + + time, + div[role='menubar'] { + pointer-events: none; + opacity: 0; + transition: opacity 200ms ${token.motionEaseOut}; + } + + &:hover { + time, + div[role='menubar'] { + pointer-events: unset; + opacity: 1; + } + } + + ${responsive.mobile} { + padding-block: ${type === 'pure' ? '12px' : '6px'}; + padding-inline: 8px; + } + ` + ), + editingContainer: cx( + editingStylish, + css` + padding-block: 8px 12px; + padding-inline: 12px; + border: 1px solid ${token.colorBorderSecondary}; + + &:active, + &:hover { + border-color: ${token.colorBorder}; + } + `, + type === 'pure' && + css` + background: ${token.colorFillQuaternary}; + border-radius: ${token.borderRadius}px; + ` + ), + editingInput: css` + width: 100%; + `, + errorContainer: css` + position: relative; + overflow: hidden; + width: 100%; + `, + + loading: css` + position: absolute; + inset-block-end: 0; + inset-inline: ${placement === 'right' ? '-4px' : 'unset'} + ${placement === 'left' ? '-4px' : 'unset'}; + + width: 16px; + height: 16px; + + color: ${token.colorBgLayout}; + + background: ${token.colorPrimary}; + border-radius: 50%; + `, + message: cx( + typeStylish, + css` + position: relative; + overflow: hidden; + max-width: 100%; + + ${responsive.mobile} { + width: 100%; + } + ` + ), + messageContainer: cx( + editingStylish, + css` + position: relative; + overflow: hidden; + max-width: 100%; + margin-block-start: ${time ? -16 : 0}px; + + ${responsive.mobile} { + overflow-x: auto; + } + ` + ), + messageContent: cx( + editingStylish, + css` + position: relative; + overflow: hidden; + max-width: 100%; + + ${responsive.mobile} { + flex-direction: column !important; + } + ` + ), + messageExtra: cx('message-extra'), + name: css` + pointer-events: none; + + margin-block-end: 6px; + + font-size: 12px; + line-height: 1; + color: ${token.colorTextDescription}; + text-align: ${placement === 'left' ? 'left' : 'right'}; + `, + }; + } +); diff --git a/src/ChatItem/type.ts b/src/ChatItem/type.ts new file mode 100644 index 0000000..7e113f4 --- /dev/null +++ b/src/ChatItem/type.ts @@ -0,0 +1,89 @@ +import { + AlertProps, + AvatarProps, + DivProps, + EditableMessageProps, + MarkdownProps, + MetaData, +} from '@lobehub/ui'; +import { ReactNode } from 'react'; + +export interface ChatItemProps { + /** + * @description Actions to be displayed in the chat item + */ + actions?: ReactNode; + /** + * @description Metadata for the avatar + */ + avatar: MetaData; + avatarAddon?: ReactNode; + avatarProps?: AvatarProps; + /** + * @description Custom CSS class name for the chat item + */ + className?: string; + /** + * @description Whether the chat item is in editing mode + */ + editing?: boolean; + /** + * @description Props for Error render + */ + error?: AlertProps; + errorMessage?: ReactNode; + fontSize?: number; + /** + * @description Whether the chat item is in loading state + */ + loading?: boolean; + /** + * @description The message content of the chat item + */ + message?: ReactNode; + messageExtra?: ReactNode; + onAvatarClick?: () => void; + /** + * @description Callback when the message content changes + * @param value - The new message content + */ + onChange?: (value: string) => void; + onDoubleClick?: DivProps['onDoubleClick']; + /** + * @description Callback when the editing mode changes + * @param editing - The new editing mode + */ + onEditingChange?: (editing: boolean) => void; + /** + * @description The placement of the chat item + * @default 'left' + */ + placement?: 'left' | 'right'; + /** + * @description Whether the chat item is primary + */ + primary?: boolean; + renderMessage?: (content: ReactNode) => ReactNode; + /** + * @description Whether to show the title of the chat item + */ + showTitle?: boolean; + text?: EditableMessageProps['text']; + /** + * @description The timestamp of the chat item + */ + time?: number; + /** + * @description The type of the chat item + * @default 'block' + */ + type?: 'block' | 'pure'; + /** + * @description The markdownProps of the chat item + */ + markdownProps?: MarkdownProps; + /** + * @description The markdownClassname of the chat item + */ + markdownClassname?: string; +} diff --git a/src/EditableMessage/demos/data.ts b/src/EditableMessage/demos/data.ts new file mode 100644 index 0000000..b4ba908 --- /dev/null +++ b/src/EditableMessage/demos/data.ts @@ -0,0 +1,167 @@ +export const content = `# This is an H1 +## This is an H2 +### This is an H3 +#### This is an H4 +##### This is an H5 + +The point of reference-style links is not that they’re easier to write. The point is that with reference-style links, your document source is vastly more readable. Compare the above examples: using reference-style links, the paragraph itself is only 81 characters long; with inline-style links, it’s 176 characters; and as raw \`HTML\`, it’s 234 characters. In the raw \`HTML\`, there’s more markup than there is text. + +--- + +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. +> +> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +> id sem consectetuer libero luctus adipiscing. + +--- + +an example | *an example* | **an example** + +--- + +![](https://gw.alipayobjects.com/zos/kitchen/sLO%24gbrQtp/lobe-chat.webp) + +![](https://gw.alipayobjects.com/zos/kitchen/8Ab%24hLJ5ur/cover.webp) + +<video + poster="https://gw.alipayobjects.com/zos/kitchen/sLO%24gbrQtp/lobe-chat.webp" + src="https://github.com/lobehub/lobe-chat/assets/28616219/f29475a3-f346-4196-a435-41a6373ab9e2"/> + +--- + +1. Bird +1. McHale +1. Parish + 1. Bird + 1. McHale + 1. Parish + +--- + +- Red +- Green +- Blue + - Red + - Green + - Blue + +--- + +This is [an example](http://example.com/ "Title") inline link. + +<http://example.com/> + + +| title | title | title | +| --- | --- | --- | +| content | content | content | + + +\`\`\`bash +$ pnpm install +\`\`\` + + +\`\`\`javascript +import { renderHook } from '@testing-library/react-hooks'; +import { act } from 'react-dom/test-utils'; +import { useDropNodeOnCanvas } from './useDropNodeOnCanvas'; +\`\`\` + +--- + +以下是一段Markdown格式的LaTeX数学公式: + +我是一个行内公式:$E=mc^2$ + +我是一个独立公式: +$$ +\\sum_{i=1}^{n} x_i = x_1 + x_2 + \\ldots + x_n +$$ + +我是一个带有分式的公式: +$$ +\\frac{{n!}}{{k!(n-k)!}} = \\binom{n}{k} +$$ + +我是一个带有上下标的公式: +$$ +x^{2} + y^{2} = r^{2} +$$ + +我是一个带有积分符号的公式: +$$ +\\int_{a}^{b} f(x) \\, dx +$$ + +--- + +我是一个嵌套测试: +\`\`\` +$1 +\`\`\` +`; + +export const content2 = `# Customize Markdown Components +#### Customize Anchor Behavior +This is [an example](http://example.com/ "Title") inline link. + +<http://example.com/> + + +#### Customize Hr + +--- + +#### Customize Image Display + +![](https://gw.alipayobjects.com/zos/kitchen/sLO%24gbrQtp/lobe-chat.webp) +`; + +export const code = ` + +#### transformerNotationDiff + +\`\`\`ts +export function foo() { + console.log('hewwo') // [!code --] + console.log('hello') // [!code ++] +} +\`\`\` + +#### transformerNotationHighlight + +\`\`\`ts +export function foo() { + console.log('Highlighted') // [!code highlight] +} +\`\`\` + +#### transformerNotationWordHighlight + +\`\`\`ts +export function foo() { // [!code word:Hello] + const msg = 'Hello World' + console.log(msg) // 打印 Hello World +} +\`\`\` + +#### transformerNotationFocus + +\`\`\`ts +export function foo() { + console.log('Focused') // [!code focus] +} +\`\`\` + +#### transformerNotationErrorLevel + +\`\`\`ts +export function foo() { + console.error('Error') // [!code error] + console.warn('Warning') // [!code warning] +} +\`\`\` +`; diff --git a/src/EditableMessage/demos/index.tsx b/src/EditableMessage/demos/index.tsx new file mode 100644 index 0000000..e702f8e --- /dev/null +++ b/src/EditableMessage/demos/index.tsx @@ -0,0 +1,36 @@ +import { StoryBook, useControls, useCreateStore } from '@lobehub/ui'; +import { button } from 'leva'; +import { useState } from 'react'; + +import EditableMessage from '..'; +import { content } from './data'; + +export default () => { + const [openModal, setOpenModal] = useState(false); + const [editing, setEdit] = useState(false); + const store = useCreateStore(); + + useControls( + { + editing: button(() => { + setEdit(true); + }), + openModal: button(() => { + setOpenModal(true); + }), + }, + { store } + ); + + return ( + <StoryBook levaStore={store}> + <EditableMessage + editing={editing} + onEditingChange={setEdit} + onOpenChange={setOpenModal} + openModal={openModal} + value={content} + /> + </StoryBook> + ); +}; diff --git a/src/EditableMessage/index.md b/src/EditableMessage/index.md new file mode 100644 index 0000000..27cd635 --- /dev/null +++ b/src/EditableMessage/index.md @@ -0,0 +1,14 @@ +--- +nav: Components +group: Chat +title: EditableMessage +description: The EditableMessage component is used to display a message that can be edited by the user. It consists of a Markdown component and an optional modal for editing the message. When the user clicks on the message, it enters editing mode and displays an input field for editing the message. +--- + +## Default + +<code src="./demos/index.tsx" nopadding></code> + +## APIs + +<API></API> diff --git a/src/EditableMessage/index.tsx b/src/EditableMessage/index.tsx new file mode 100644 index 0000000..58e4570 --- /dev/null +++ b/src/EditableMessage/index.tsx @@ -0,0 +1,184 @@ +'use client'; + +import { + Markdown, + MarkdownProps, + MessageInput, + type MessageInputProps, + MessageModal, + type MessageModalProps, +} from '@lobehub/ui'; +import { CSSProperties, memo } from 'react'; +import useControlledState from 'use-merge-value'; + +export interface EditableMessageProps { + /** + * @title The class name for the Markdown and MessageInput component + */ + classNames?: { + /** + * @title The class name for the MessageInput component + */ + input?: string; + /** + * @title The class name for the Markdown component + */ + markdown?: string; + textarea?: string; + }; + editButtonSize?: MessageInputProps['editButtonSize']; + /** + * @title Whether the component is in edit mode or not + * @default false + */ + editing?: boolean; + fontSize?: number; + fullFeaturedCodeBlock?: boolean; + height?: MessageInputProps['height']; + inputType?: MessageInputProps['type']; + model?: { + extra?: MessageModalProps['extra']; + footer?: MessageModalProps['footer']; + }; + /** + * @title Callback function when the value changes + * @param value - The new value + */ + onChange?: (value: string) => void; + /** + * @title Callback function when the editing state changes + * @param editing - Whether the component is in edit mode or not + */ + onEditingChange?: (editing: boolean) => void; + /** + * @title Callback function when the modal open state changes + * @param open - Whether the modal is open or not + */ + onOpenChange?: (open: boolean) => void; + /** + * @title Whether the modal is open or not + * @default false + */ + openModal?: boolean; + placeholder?: string; + /** + * @title Whether to show the edit button when the text value is empty + * @default false + */ + showEditWhenEmpty?: boolean; + styles?: { + /** + * @title The style for the MessageInput component + */ + input?: CSSProperties; + /** + * @title The style for the Markdown component + */ + markdown?: CSSProperties; + }; + text?: MessageModalProps['text']; + /** + * @title The current text value + */ + value: string; + markdownProps?: MarkdownProps; +} + +const EditableMessage = memo<EditableMessageProps>( + ({ + value, + onChange, + classNames = {}, + onEditingChange, + editing, + openModal, + onOpenChange, + placeholder, + showEditWhenEmpty = false, + styles: stylesProps, + height, + inputType, + editButtonSize, + text, + fullFeaturedCodeBlock, + model, + fontSize, + markdownProps, + }) => { + const [isEdit, setTyping] = useControlledState(false, { + onChange: onEditingChange, + value: editing, + }); + + const [expand, setExpand] = useControlledState<boolean>(false, { + onChange: onOpenChange, + value: openModal, + }); + + const isAutoSize = height === 'auto'; + + const input = ( + <MessageInput + className={classNames?.input} + classNames={{ textarea: classNames?.textarea }} + defaultValue={value} + editButtonSize={editButtonSize} + height={height} + onCancel={() => setTyping(false)} + onConfirm={text => { + onChange?.(text); + setTyping(false); + }} + placeholder={placeholder} + style={stylesProps?.input} + text={text} + textareaClassname={classNames?.input} + type={inputType} + /> + ); + + if (!value && showEditWhenEmpty) return input; + + return ( + <> + {!expand && isEdit ? ( + input + ) : ( + <Markdown + className={classNames?.markdown} + fontSize={fontSize} + fullFeaturedCodeBlock={fullFeaturedCodeBlock} + style={{ + height: isAutoSize ? 'unset' : height, + ...stylesProps?.markdown, + }} + variant={'chat'} + {...markdownProps} + > + {value || placeholder || ''} + </Markdown> + )} + {expand && ( + <MessageModal + editing={isEdit} + extra={model?.extra} + footer={model?.footer} + height={height} + onChange={onChange} + onEditingChange={setTyping} + onOpenChange={e => { + setExpand(e); + setTyping(false); + }} + open={expand} + placeholder={placeholder} + text={text} + value={value} + /> + )} + </> + ); + } +); + +export default EditableMessage; diff --git a/src/index.ts b/src/index.ts index 908ffca..da8d285 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,6 +30,8 @@ export * from './Typography'; // ~ custom @lobehub/ui export * from './ChatInputArea'; +export * from './ChatItem'; +export * from './EditableMessage'; export * from './Highlighter'; export * from './styles';