@baishuyun/ai-design 2.0.57 → 2.0.61

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
@@ -1,5 +1,32 @@
1
1
  # @baishuyun/ai-design
2
2
 
3
+ ## 2.0.61
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @baishuyun/ai-feat-utils@2.0.55
9
+
10
+ ## 2.0.60
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+ - @baishuyun/ai-feat-utils@2.0.54
16
+
17
+ ## 2.0.59
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies
22
+ - @baishuyun/ai-feat-utils@2.0.53
23
+
24
+ ## 2.0.58
25
+
26
+ ### Patch Changes
27
+
28
+ - 4b49068: 用户文本气泡超过 200 字可关闭折叠,折叠条图标可隐藏,正文保留换行并解析 Markdown。
29
+
3
30
  ## 2.0.57
4
31
 
5
32
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baishuyun/ai-design",
3
- "version": "2.0.57",
3
+ "version": "2.0.61",
4
4
  "description": "AI 功能 UI 组件库 - 所有 AI 功能的 UI 表现唯一真相来源",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -55,12 +55,13 @@
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/ai-feat-utils": "2.0.52",
64
+ "@baishuyun/ai-feat-utils": "2.0.55",
64
65
  "@baishuyun/types": "3.0.48"
65
66
  },
66
67
  "peerDependencies": {
@@ -7,7 +7,8 @@ import classNames from 'classnames';
7
7
  export interface CollapsibleTxtMsgProps {
8
8
  title: string;
9
9
  content?: string;
10
- icon?: ReactNode;
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
- <div className={classNames("shrink-0 flex items-center", iconSizeCls)}>{icon || <AnalysisIcon />}</div>
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 <ReactMarkdown>{content}</ReactMarkdown>;
13
- },
14
- (prevProps, nextProps) => {
15
- if (prevProps.content !== nextProps.content) return false;
16
- return true;
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 content={block} key={`${id}-block_${index}`} />
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 = ({ text, children, isMobile }: UserTextMsgBubbleProps) => {
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 icon={<TextIcon />} ghost isMobile={isMobile} title={summary}>
44
- {content}
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
- content
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
+ };