@baishuyun/ai-design 2.0.57 → 2.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baishuyun/ai-design",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.58",
|
|
4
4
|
"description": "AI 功能 UI 组件库 - 所有 AI 功能的 UI 表现唯一真相来源",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -55,13 +55,14 @@
|
|
|
55
55
|
"react-day-picker": "^9.11.2",
|
|
56
56
|
"react-hook-form": "^7.76.0",
|
|
57
57
|
"react-markdown": "^10.1.0",
|
|
58
|
+
"remark-breaks": "^4.0.0",
|
|
58
59
|
"tailwind-merge": "^3.4.0",
|
|
59
60
|
"tailwindcss-animate": "^1.0.7",
|
|
60
61
|
"tw-animate-css": "^1.4.0",
|
|
61
62
|
"vaul": "^1.1.2",
|
|
62
63
|
"zod": "^4.1.13",
|
|
63
|
-
"@baishuyun/
|
|
64
|
-
"@baishuyun/
|
|
64
|
+
"@baishuyun/types": "3.0.48",
|
|
65
|
+
"@baishuyun/ai-feat-utils": "2.0.52"
|
|
65
66
|
},
|
|
66
67
|
"peerDependencies": {
|
|
67
68
|
"react": "^19.0.0",
|
|
@@ -7,7 +7,8 @@ import classNames from 'classnames';
|
|
|
7
7
|
export interface CollapsibleTxtMsgProps {
|
|
8
8
|
title: string;
|
|
9
9
|
content?: string;
|
|
10
|
-
|
|
10
|
+
/** 不传时用 AnalysisIcon。null 不显示图标 */
|
|
11
|
+
icon?: ReactNode | null;
|
|
11
12
|
defaultOpen?: boolean;
|
|
12
13
|
children?: ReactNode;
|
|
13
14
|
|
|
@@ -40,6 +41,7 @@ export const CollapsibleTxtMsg = ({
|
|
|
40
41
|
const isGhostOpen = isOpen && ghost;
|
|
41
42
|
|
|
42
43
|
const t = isGhostOpen ? '' : title;
|
|
44
|
+
const shownIcon = icon === undefined ? <AnalysisIcon /> : icon;
|
|
43
45
|
|
|
44
46
|
return (
|
|
45
47
|
<div className={classNames("flex flex-col gap-2.5 rounded-lg w-full", cls)}>
|
|
@@ -54,7 +56,9 @@ export const CollapsibleTxtMsg = ({
|
|
|
54
56
|
}}
|
|
55
57
|
>
|
|
56
58
|
<div className="flex gap-2.5 items-center">
|
|
57
|
-
|
|
59
|
+
{shownIcon ? (
|
|
60
|
+
<div className={classNames("shrink-0 flex items-center", iconSizeCls)}>{shownIcon}</div>
|
|
61
|
+
) : null}
|
|
58
62
|
<p className={`text-[#666] ${sizeCls} leading-normal`}>{t}</p>
|
|
59
63
|
</div>
|
|
60
64
|
<button
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { marked } from "marked";
|
|
2
2
|
import { memo, useMemo } from "react";
|
|
3
3
|
import ReactMarkdown from "react-markdown";
|
|
4
|
+
import remarkBreaks from "remark-breaks";
|
|
4
5
|
|
|
5
6
|
function parseMarkdownIntoBlocks(markdown: string): string[] {
|
|
6
7
|
const tokens = marked.lexer(markdown);
|
|
@@ -8,23 +9,27 @@ function parseMarkdownIntoBlocks(markdown: string): string[] {
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
const MemoizedMarkdownBlock = memo(
|
|
11
|
-
({ content }: { content: string }) => {
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
({ content, breaks }: { content: string; breaks?: boolean }) => {
|
|
13
|
+
return (
|
|
14
|
+
<ReactMarkdown remarkPlugins={breaks ? [remarkBreaks] : undefined}>
|
|
15
|
+
{content}
|
|
16
|
+
</ReactMarkdown>
|
|
17
|
+
);
|
|
17
18
|
},
|
|
18
19
|
);
|
|
19
20
|
|
|
20
21
|
MemoizedMarkdownBlock.displayName = "MemoizedMarkdownBlock";
|
|
21
22
|
|
|
22
23
|
export const MemoizedMarkdown = memo(
|
|
23
|
-
({ content, id }: { content: string; id: string }) => {
|
|
24
|
+
({ content, id, breaks }: { content: string; id: string; breaks?: boolean }) => {
|
|
24
25
|
const blocks = useMemo(() => parseMarkdownIntoBlocks(content), [content]);
|
|
25
26
|
|
|
26
27
|
return blocks.map((block, index) => (
|
|
27
|
-
<MemoizedMarkdownBlock
|
|
28
|
+
<MemoizedMarkdownBlock
|
|
29
|
+
breaks={breaks}
|
|
30
|
+
content={block}
|
|
31
|
+
key={`${id}-block_${index}`}
|
|
32
|
+
/>
|
|
28
33
|
));
|
|
29
34
|
},
|
|
30
35
|
);
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
1
2
|
import { TextIcon } from './bs-icons';
|
|
2
3
|
import { CollapsibleTxtMsg } from './collapsible-txt-msg';
|
|
4
|
+
import { MemoizedMarkdown } from './memoized-markdown';
|
|
3
5
|
|
|
4
6
|
export interface UserTextMsgBubbleProps {
|
|
5
7
|
text?: string;
|
|
6
8
|
children?: string;
|
|
7
9
|
/** 移动端消息内容文字 16px(PC 端 14px) */
|
|
8
10
|
isMobile?: boolean;
|
|
11
|
+
/** 超过 200 字时折叠为前 10 个字。默认开启 */
|
|
12
|
+
collapsible?: boolean;
|
|
13
|
+
/** 折叠条图标。不传为 TextIcon,null 不显示图标 */
|
|
14
|
+
icon?: ReactNode | null;
|
|
9
15
|
}
|
|
10
16
|
|
|
11
|
-
export const UserTextMsgBubble = ({
|
|
17
|
+
export const UserTextMsgBubble = ({
|
|
18
|
+
text,
|
|
19
|
+
children,
|
|
20
|
+
isMobile,
|
|
21
|
+
collapsible = true,
|
|
22
|
+
icon,
|
|
23
|
+
}: UserTextMsgBubbleProps) => {
|
|
12
24
|
const content = (text || children) as string;
|
|
13
25
|
const sizeCls = isMobile ? 'text-[16px]' : 'text-[14px]';
|
|
14
26
|
const paddingCls = isMobile ? "px-[10px] py-[9px]" : "px-2.5 py-[8px]";
|
|
@@ -30,6 +42,7 @@ export const UserTextMsgBubble = ({ text, children, isMobile }: UserTextMsgBubbl
|
|
|
30
42
|
|
|
31
43
|
const isTooLong = content.length > 200;
|
|
32
44
|
const summary = `${content?.slice(0, 10)}...`;
|
|
45
|
+
const body = <MemoizedMarkdown breaks content={content} id="user-txt-msg" />;
|
|
33
46
|
return (
|
|
34
47
|
<div
|
|
35
48
|
className={`${paddingCls} text-[#0265FF] ${sizeCls} w-fit`}
|
|
@@ -39,12 +52,17 @@ export const UserTextMsgBubble = ({ text, children, isMobile }: UserTextMsgBubbl
|
|
|
39
52
|
background: `linear-gradient(180deg, #DBE9FF 0%, #E9FFFD 100%)`,
|
|
40
53
|
}}
|
|
41
54
|
>
|
|
42
|
-
{isTooLong ? (
|
|
43
|
-
<CollapsibleTxtMsg
|
|
44
|
-
{
|
|
55
|
+
{isTooLong && collapsible ? (
|
|
56
|
+
<CollapsibleTxtMsg
|
|
57
|
+
icon={icon === undefined ? <TextIcon /> : icon}
|
|
58
|
+
ghost
|
|
59
|
+
isMobile={isMobile}
|
|
60
|
+
title={summary}
|
|
61
|
+
>
|
|
62
|
+
{body}
|
|
45
63
|
</CollapsibleTxtMsg>
|
|
46
64
|
) : (
|
|
47
|
-
|
|
65
|
+
body
|
|
48
66
|
)}
|
|
49
67
|
</div>
|
|
50
68
|
);
|
|
@@ -2,6 +2,8 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
|
2
2
|
|
|
3
3
|
import { UserTextMsgBubble } from "../components/user-txt-msg-bubble";
|
|
4
4
|
|
|
5
|
+
const longText = "供应商信息管理表需要先填写基础资料再补充产品明细。".repeat(9);
|
|
6
|
+
|
|
5
7
|
const meta = {
|
|
6
8
|
title: "Example/Chat/UserTextMsgBubble",
|
|
7
9
|
component: UserTextMsgBubble,
|
|
@@ -22,3 +24,29 @@ export const Base: Story = {
|
|
|
22
24
|
text: "确认搭建:供应商信息管理表",
|
|
23
25
|
},
|
|
24
26
|
};
|
|
27
|
+
|
|
28
|
+
export const Long: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
text: longText,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const CollapseDisabled: Story = {
|
|
35
|
+
args: {
|
|
36
|
+
text: longText,
|
|
37
|
+
collapsible: false,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const NoIcon: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
text: longText,
|
|
44
|
+
icon: null,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const WithMarkdown: Story = {
|
|
49
|
+
args: {
|
|
50
|
+
text: "请按下面格式填写:\n名称:供应商\n\n```ts\nconst name = \"供应商\";\n```",
|
|
51
|
+
},
|
|
52
|
+
};
|