@baishuyun/chat-sdk 0.0.10 → 0.0.12

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/package.json +4 -4
  3. package/src/chat.tsx +20 -1
  4. package/src/components/biz-comp/FieldChecker.tsx +23 -2
  5. package/src/components/biz-comp/FieldCheckerListMsg.tsx +30 -5
  6. package/src/components/biz-comp/FieldValueChecker.tsx +27 -1
  7. package/src/components/biz-comp/SubformFieldsValueChecker.tsx +22 -1
  8. package/src/components/biz-comp/chat-client.tsx +13 -6
  9. package/src/components/biz-comp/field-icon.tsx +1 -1
  10. package/src/components/biz-comp/multi-modal-input/index.tsx +31 -34
  11. package/src/components/biz-comp/multi-modal-input/prompt-input.tsx +1 -4
  12. package/src/components/biz-comp/opening-lines.tsx +1 -1
  13. package/src/components/biz-comp/preview-message-wrapper.tsx +8 -1
  14. package/src/components/biz-comp/preview-message.tsx +17 -1
  15. package/src/components/bs-ui/attachments-previewer.tsx +124 -32
  16. package/src/components/bs-ui/bot-avatar-name.tsx +1 -1
  17. package/src/components/bs-ui/bs-icons.tsx +15 -1
  18. package/src/components/bs-ui/card.tsx +1 -1
  19. package/src/components/bs-ui/chat-area-header.tsx +12 -2
  20. package/src/components/bs-ui/collapsible-txt-msg.tsx +1 -1
  21. package/src/components/bs-ui/fields-previewer.tsx +5 -0
  22. package/src/components/bs-ui/form-info-editor.tsx +2 -3
  23. package/src/components/bs-ui/icon-btn.tsx +11 -4
  24. package/src/components/bs-ui/line-checker.tsx +31 -14
  25. package/src/components/bs-ui/previewer-header.tsx +33 -6
  26. package/src/components/bs-ui/primary-confirm-btn.tsx +10 -5
  27. package/src/components/bs-ui/primary-entry-btn.tsx +1 -1
  28. package/src/components/bs-ui/square-checker.tsx +62 -0
  29. package/src/components/bs-ui/tab-radio-group.tsx +12 -3
  30. package/src/components/bs-ui/tooltip.tsx +37 -2
  31. package/src/components/ui/tooltip.tsx +37 -18
  32. package/src/const/index.ts +7 -1
  33. package/src/index.tsx +1 -1
  34. package/src/plugins/{mcp-form-builder-plugin → form-builder-plugin}/components/create-form-confirm.tsx +3 -2
  35. package/src/plugins/form-builder-plugin/components/entry-btn.tsx +44 -0
  36. package/src/plugins/{mcp-form-builder-plugin → form-builder-plugin}/components/msg-part.tsx +44 -27
  37. package/src/plugins/{mcp-form-builder-plugin → form-builder-plugin}/index.ts +19 -5
  38. package/src/plugins/form-filling-plugin/components/FormFillingOpeningLines.tsx +2 -1
  39. package/src/plugins/form-filling-plugin/components/avatar.tsx +9 -3
  40. package/src/plugins/form-filling-plugin/components/mode-select.tsx +10 -2
  41. package/src/plugins/form-filling-plugin/components/msg-part.tsx +26 -1
  42. package/src/plugins/form-filling-plugin/const.ts +1 -1
  43. package/src/plugins/form-filling-plugin/index.ts +18 -0
  44. package/src/plugins/report-query-plugin/components/query-msg-part.tsx +1 -0
  45. package/src/plugins/report-query-plugin/components/query-opening-lines.tsx +30 -0
  46. package/src/plugins/report-query-plugin/components/result-cards/DataTableCard.tsx +0 -1
  47. package/src/plugins/report-query-plugin/index.ts +11 -0
  48. package/src/plugins/report-query-plugin/utils/build-dash-component.ts +122 -0
  49. package/src/plugins/report-query-plugin/utils/create-default-dash-styles.ts +29 -0
  50. package/src/plugins/report-query-plugin/utils/create-default-widget-attr-list.ts +59 -0
  51. package/src/plugins/report-query-plugin/utils/field-enhance.ts +62 -0
  52. package/src/plugins/report-query-plugin/utils/index.tsx +109 -0
  53. package/src/stories/AttachmentsPreviewer.stories.tsx +118 -0
  54. package/src/stories/PrimaryConfirmBtn.stories.tsx +7 -0
  55. package/src/stories/SquareChecker.stories.tsx +96 -0
  56. package/src/style.css +24 -0
  57. package/dist/chat-sdk.js +0 -58300
  58. package/dist/chat-sdk.js.map +0 -1
  59. package/dist/chat-sdk.umd.cjs +0 -663
  60. package/dist/chat-sdk.umd.cjs.map +0 -1
  61. package/dist/index.css +0 -1
  62. package/src/plugins/mcp-form-builder-plugin/components/entry-btn.tsx +0 -9
  63. package/src/plugins/report-query-plugin/utils.tsx +0 -379
  64. /package/src/plugins/{mcp-form-builder-plugin → form-builder-plugin}/components/follow-up.tsx +0 -0
  65. /package/src/plugins/{mcp-form-builder-plugin → form-builder-plugin}/const/index.ts +0 -0
  66. /package/src/plugins/{mcp-form-builder-plugin → form-builder-plugin}/hooks/index.ts +0 -0
  67. /package/src/plugins/{mcp-form-builder-plugin → form-builder-plugin}/hooks/use-fields-confirmed.ts +0 -0
  68. /package/src/plugins/{mcp-form-builder-plugin → form-builder-plugin}/types.ts +0 -0
  69. /package/src/plugins/{mcp-form-builder-plugin → form-builder-plugin}/utils/index.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @baishuyun/chat-sdk
2
2
 
3
+ ## 0.0.12
4
+
5
+ ### Patch Changes
6
+
7
+ - udpate style
8
+ - Updated dependencies
9
+ - @baishuyun/agents@0.0.12
10
+
11
+ ## 0.0.11
12
+
13
+ ### Patch Changes
14
+
15
+ - style update
16
+ - Updated dependencies
17
+ - @baishuyun/agents@0.0.11
18
+
3
19
  ## 0.0.10
4
20
 
5
21
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baishuyun/chat-sdk",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "",
5
5
  "main": "src/index.jsx",
6
6
  "module": "dist/chat-sdk.js",
@@ -35,7 +35,7 @@
35
35
  "tw-animate-css": "^1.4.0",
36
36
  "use-stick-to-bottom": "^1.1.1",
37
37
  "zustand": "^5.0.8",
38
- "@baishuyun/agents": "0.0.10"
38
+ "@baishuyun/agents": "0.0.12"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@storybook/react-vite": "^10.1.11",
@@ -51,8 +51,8 @@
51
51
  "tailwindcss": "^4.1.17",
52
52
  "vite": "^5.1.4",
53
53
  "vite-plugin-dts": "^4.5.4",
54
- "@baishuyun/types": "1.0.10",
55
- "@baishuyun/typescript-config": "0.0.10"
54
+ "@baishuyun/types": "1.0.12",
55
+ "@baishuyun/typescript-config": "0.0.12"
56
56
  },
57
57
  "exports": {
58
58
  ".": {
package/src/chat.tsx CHANGED
@@ -2,7 +2,7 @@ import { DefaultChatTransport } from 'ai';
2
2
  import { useEffect, useMemo, useState } from 'react';
3
3
  import { ChatFrame } from '@/components/biz-comp/chat-frame';
4
4
  import { Messages } from '@/components/biz-comp/messages';
5
- import { Attachment, ChatMessage } from '@baishuyun/types';
5
+ import { Attachment, ChatMessage, IGhostMsgEventDetail } from '@baishuyun/types';
6
6
  import { ChatSDK } from '@/.';
7
7
  import { MultimodalInput } from './components/biz-comp/multi-modal-input';
8
8
  import { useDefaultPluginCustomComponent } from './hooks/use-plugin-custom-components';
@@ -10,10 +10,14 @@ import { useMergedChat } from './hooks/use-merged-chat';
10
10
  import { useChatPreference } from './hooks/use-chat-preference';
11
11
  import { useMsgStatusBroadcast } from './hooks/use-msg-status-broadcast';
12
12
  import { useChatStatus } from './hooks/use-frame-mode';
13
+ import { usePluginLifeCycleChainRunner } from './hooks/use-plugin-life-cycle-chain-runner';
14
+ import { useEvt } from './hooks/use-evt';
13
15
 
14
16
  export const Chat = ({ client }: { client: ChatSDK }) => {
15
17
  const preference = useChatPreference(client);
16
18
 
19
+ const { exec: onAfterChatHide = () => {} } = usePluginLifeCycleChainRunner('onAfterChatAreaHide');
20
+
17
21
  const transport = useMemo(() => {
18
22
  return new DefaultChatTransport({
19
23
  api: client?.options?.chatApiEndpoint,
@@ -29,11 +33,26 @@ export const Chat = ({ client }: { client: ChatSDK }) => {
29
33
  enableMerge: preference?.enableMessageMerge,
30
34
  });
31
35
 
36
+ useEvt('send-ghost-msg', (detail) => {
37
+ const { text, options } = detail as IGhostMsgEventDetail;
38
+ sendMessage(
39
+ {
40
+ text,
41
+ },
42
+ options
43
+ );
44
+ });
45
+
32
46
  const { isHide } = useChatStatus();
33
47
 
34
48
  useEffect(() => {
35
49
  if (isHide) {
36
50
  stop();
51
+ onAfterChatHide({
52
+ clearMsgs: () => {
53
+ setMessages(() => []);
54
+ },
55
+ });
37
56
  }
38
57
  }, [isHide, stop]);
39
58
 
@@ -16,11 +16,23 @@ export interface FieldCheckerProps {
16
16
  icon?: React.ReactNode;
17
17
  onChange?: (checked: boolean, field: Field, parentFieldName?: string, index?: number) => void;
18
18
  value?: unknown;
19
+ streaming?: boolean;
20
+ confirmed?: boolean;
19
21
  onLoaded?: (field: Field) => void;
20
22
  }
21
23
 
22
24
  export const FieldChecker = memo(
23
- ({ field, disabled, isSubField, onChange, onLoaded, icon, value }: FieldCheckerProps) => {
25
+ ({
26
+ field,
27
+ disabled,
28
+ isSubField,
29
+ onChange,
30
+ onLoaded,
31
+ icon,
32
+ value,
33
+ streaming,
34
+ confirmed,
35
+ }: FieldCheckerProps) => {
24
36
  const { widget, label } = field;
25
37
  if (!widget) {
26
38
  return null;
@@ -68,6 +80,8 @@ export const FieldChecker = memo(
68
80
  return (
69
81
  <>
70
82
  <LineChecker
83
+ streaming={streaming}
84
+ confirmed={confirmed}
71
85
  title={label}
72
86
  disabled={disabled}
73
87
  defaultChecked={true}
@@ -90,6 +104,8 @@ export const FieldChecker = memo(
90
104
  {isSubForm
91
105
  ? widget.items.map((subField: Field, index: number) => (
92
106
  <FieldChecker
107
+ confirmed={confirmed}
108
+ streaming={streaming}
93
109
  key={subField.label}
94
110
  isSubField={true}
95
111
  disabled={disabled}
@@ -106,6 +122,11 @@ export const FieldChecker = memo(
106
122
  );
107
123
  },
108
124
  (p1, p2) => {
109
- return p1.field?.label === p2.field?.label && p1.disabled === p2.disabled;
125
+ return (
126
+ p1.field?.label === p2.field?.label &&
127
+ p1.disabled === p2.disabled &&
128
+ p1.confirmed === p2.confirmed &&
129
+ p1.streaming === p2.streaming
130
+ );
110
131
  }
111
132
  );
@@ -5,27 +5,52 @@ import { EditIcon, LetterAIcon } from '../bs-ui/bs-icons';
5
5
  import { FieldValueChecker, FieldValueCheckerProps } from './FieldValueChecker';
6
6
 
7
7
  export const FieldCheckerListMsg = memo(
8
- ({ fields, children }: { fields: Array<FieldCheckerProps>; children?: ReactNode }) => {
8
+ ({
9
+ fields,
10
+ children,
11
+ streaming,
12
+ confirmed,
13
+ }: {
14
+ fields: Array<FieldCheckerProps>;
15
+ children?: ReactNode;
16
+ streaming: boolean;
17
+ confirmed?: boolean;
18
+ }) => {
9
19
  return (
10
20
  <CollapsibleTxtMsg title="字段生成" icon={<LetterAIcon />} defaultOpen>
11
21
  {fields.map((f) => (
12
- <FieldChecker {...f} />
22
+ <FieldChecker {...f} streaming={streaming} confirmed={confirmed} />
13
23
  ))}
14
24
  {children}
15
25
  </CollapsibleTxtMsg>
16
26
  );
17
27
  },
18
28
  (p1, p2) => {
19
- return p1.fields.length === p2.fields.length;
29
+ return (
30
+ p1.fields.length === p2.fields.length &&
31
+ p1.streaming === p2.streaming &&
32
+ p1.confirmed === p2.confirmed &&
33
+ p1.children === p2.children
34
+ );
20
35
  }
21
36
  );
22
37
 
23
38
  export const FieldValueCheckerListMsg = memo(
24
- ({ props, children }: { props: Array<FieldValueCheckerProps>; children?: ReactNode }) => {
39
+ ({
40
+ props,
41
+ readonly,
42
+ children,
43
+ streaming,
44
+ }: {
45
+ props: Array<FieldValueCheckerProps>;
46
+ children?: ReactNode;
47
+ readonly?: boolean;
48
+ streaming?: boolean;
49
+ }) => {
25
50
  return (
26
51
  <CollapsibleTxtMsg title="字段填写" icon={<EditIcon />} defaultOpen>
27
52
  {props.map((f) => (
28
- <FieldValueChecker {...f} />
53
+ <FieldValueChecker {...f} readonly={readonly} streaming={streaming} />
29
54
  ))}
30
55
  {children}
31
56
  </CollapsibleTxtMsg>
@@ -14,12 +14,22 @@ export interface FieldValueCheckerProps {
14
14
  field: ValueOf<Pick<ToolUIPart<FieldsTools>, 'output'>>;
15
15
  isSubField?: boolean;
16
16
  disabled?: boolean;
17
+ readonly?: boolean;
17
18
  onChange?: (checked: boolean, field: Field, parentFieldName?: string, index?: number) => void;
18
19
  onLoaded?: (field: Field) => void;
20
+ streaming?: boolean;
19
21
  }
20
22
 
21
23
  export const FieldValueChecker = memo(
22
- ({ field, disabled, isSubField, onChange, onLoaded }: FieldValueCheckerProps) => {
24
+ ({
25
+ field,
26
+ disabled,
27
+ isSubField,
28
+ readonly,
29
+ streaming,
30
+ onChange,
31
+ onLoaded,
32
+ }: FieldValueCheckerProps) => {
23
33
  const { widget, label } = field;
24
34
  if (!widget) {
25
35
  return null;
@@ -52,20 +62,24 @@ export const FieldValueChecker = memo(
52
62
  return (
53
63
  <>
54
64
  <LineChecker
65
+ streaming={streaming}
55
66
  title={label}
56
67
  disabled={disabled}
57
68
  defaultChecked={true}
58
69
  onCheckedChange={handleCheck}
59
70
  className={extraPadding}
71
+ readonly={readonly}
60
72
  shortDesc={isSubForm ? '' : (value as string)}
61
73
  // extra={typeDesc}
62
74
  />
63
75
  {isSubForm && (
64
76
  <SubformFieldsValueChecker
77
+ readonly={readonly}
65
78
  subformField={field}
66
79
  onChange={(detail) => {
67
80
  onChange?.(detail.checked, detail.field, detail.parentFieldName, detail.row);
68
81
  }}
82
+ streaming={streaming}
69
83
  />
70
84
  )}
71
85
  </>
@@ -90,6 +104,18 @@ export const FieldValueChecker = memo(
90
104
  return false;
91
105
  }
92
106
 
107
+ // 只读状态一致
108
+ const isReadonlyEqual = p1.readonly === p2.readonly;
109
+ if (!isReadonlyEqual) {
110
+ return false;
111
+ }
112
+
113
+ // 流式状态一致
114
+ const isStreamingEqual = p1.streaming === p2.streaming;
115
+ if (!isStreamingEqual) {
116
+ return false;
117
+ }
118
+
93
119
  // 进一步比较子表单下的字段
94
120
  return isSubformFieldEqual(p1?.field, p2?.field);
95
121
  }
@@ -13,6 +13,8 @@ const Checker = ({
13
13
 
14
14
  label,
15
15
  val,
16
+ streaming,
17
+ readonly,
16
18
  // desc,
17
19
  }: {
18
20
  label: string;
@@ -20,9 +22,13 @@ const Checker = ({
20
22
  onCheckedChange: (v: boolean) => void;
21
23
  disabled?: boolean;
22
24
  desc: string;
25
+ readonly?: boolean;
26
+ streaming?: boolean;
23
27
  }) => {
24
28
  return (
25
29
  <LineChecker
30
+ streaming={streaming}
31
+ readonly={readonly}
26
32
  title={label}
27
33
  disabled={disabled}
28
34
  defaultChecked={true}
@@ -37,12 +43,16 @@ const Checker = ({
37
43
  const SubFieldChecker = memo(
38
44
  ({
39
45
  field,
46
+ readonly,
40
47
  onCheckedChange,
48
+ streaming,
41
49
  }: {
42
50
  onCheckedChange: (
43
51
  checked: boolean,
44
52
  field: ValueOf<Pick<ToolUIPart<FieldsTools>, 'output'>>
45
53
  ) => void;
54
+ readonly?: boolean;
55
+ streaming?: boolean;
46
56
  field: ValueOf<Pick<ToolUIPart<FieldsTools>, 'output'>>;
47
57
  }) => {
48
58
  const { widget, label } = field;
@@ -74,6 +84,8 @@ const SubFieldChecker = memo(
74
84
 
75
85
  return (
76
86
  <Checker
87
+ streaming={streaming}
88
+ readonly={readonly}
77
89
  desc={typeDesc}
78
90
  label={label}
79
91
  val={value as string}
@@ -82,7 +94,10 @@ const SubFieldChecker = memo(
82
94
  );
83
95
  },
84
96
  (p1, p2) => {
85
- const isNameEqual = p1.field.widget?.widgetName === p2.field.widget?.widgetName;
97
+ const isNameEqual =
98
+ p1.field.widget?.widgetName === p2.field.widget?.widgetName &&
99
+ p1.readonly === p2.readonly &&
100
+ p1.streaming === p2.streaming;
86
101
  return isNameEqual;
87
102
  }
88
103
  );
@@ -110,9 +125,13 @@ type onSubformFieldChangeParam = {
110
125
  export const SubformFieldsValueChecker = ({
111
126
  subformField,
112
127
  onChange,
128
+ streaming,
129
+ readonly,
113
130
  }: {
114
131
  onChange: (p: onSubformFieldChangeParam) => void;
115
132
  subformField: ValueOf<Pick<ToolUIPart<FieldsTools>, 'output'>>;
133
+ streaming?: boolean;
134
+ readonly?: boolean;
116
135
  }) => {
117
136
  if (!subformField.widget.value || !subformField.widget.value.length) {
118
137
  return null;
@@ -135,6 +154,8 @@ export const SubformFieldsValueChecker = ({
135
154
  {rowData.map((fData: generatedField, col: number) => {
136
155
  return (
137
156
  <SubFieldChecker
157
+ readonly={readonly}
158
+ streaming={streaming}
138
159
  field={{
139
160
  label: fData.label,
140
161
  widget: {
@@ -14,6 +14,7 @@ import { useDefaultPluginCustomComponent } from '@/hooks/use-plugin-custom-compo
14
14
  import { BotAvatarAndName } from '../bs-ui/bot-avatar-name';
15
15
  import { ChatEntryButtonConfig } from '@baishuyun/types';
16
16
  import { useDraggable } from '@/hooks/use-draggable';
17
+ import { TooltipProvider } from '../ui/tooltip';
17
18
 
18
19
  const ChatSlot = ({ client }: { client?: ChatSDK }) => {
19
20
  const { setStatus, isVisible, isFloat, isDock } = useChatStatus();
@@ -105,7 +106,7 @@ const ChatContent: React.FC<{ client?: ChatSDK }> = ({ client }): ReactPortal |
105
106
  return createPortal(<ChatSlot client={client} />, chatContentEl) as ReactPortal;
106
107
  };
107
108
 
108
- export const ChatEntryBtn = ({ client }: { client?: ChatSDK }) => {
109
+ export const ChatEntryBtn = ({ client }: { client: ChatSDK }) => {
109
110
  const { bottom = 10, right = 10 } = client?.options?.btnOffset || {};
110
111
 
111
112
  const { entryVisible, entryWrapper, setStatus, isVisible, entryVariant } = useChatStore(
@@ -137,6 +138,7 @@ export const ChatEntryBtn = ({ client }: { client?: ChatSDK }) => {
137
138
 
138
139
  const EntryJsx = EntryCom ? (
139
140
  <EntryCom
141
+ client={client}
140
142
  variant={entryVariant}
141
143
  chatVisible={isVisible}
142
144
  style={entryStyle}
@@ -186,7 +188,7 @@ const MultiEntryButtonItem = ({
186
188
  client,
187
189
  config,
188
190
  }: {
189
- client?: ChatSDK;
191
+ client: ChatSDK;
190
192
  config: ChatEntryButtonConfig;
191
193
  }) => {
192
194
  const { setStatus, isVisible } = useChatStore((store) => ({
@@ -213,6 +215,7 @@ const MultiEntryButtonItem = ({
213
215
 
214
216
  const EntryJsx = EntryCom ? (
215
217
  <EntryCom
218
+ client={client}
216
219
  variant={config.variant}
217
220
  chatVisible={isVisible}
218
221
  style={entryStyle}
@@ -249,7 +252,7 @@ const MultiEntryButtonItem = ({
249
252
  };
250
253
 
251
254
  /** Renders all multi-entry buttons from chatEntryButtons Map */
252
- const MultiEntryButtons = ({ client }: { client?: ChatSDK }) => {
255
+ const MultiEntryButtons = ({ client }: { client: ChatSDK }) => {
253
256
  const chatEntryButtons = useChatStore((store) => store.chatEntryButtons);
254
257
 
255
258
  const buttons = Array.from(chatEntryButtons.values());
@@ -267,7 +270,7 @@ const MultiEntryButtons = ({ client }: { client?: ChatSDK }) => {
267
270
  );
268
271
  };
269
272
 
270
- export const ChatWidgets = ({ client }: { client?: ChatSDK }) => {
273
+ export const ChatWidgets = ({ client }: { client: ChatSDK }) => {
271
274
  return (
272
275
  <>
273
276
  <ChatContent client={client} />
@@ -277,10 +280,14 @@ export const ChatWidgets = ({ client }: { client?: ChatSDK }) => {
277
280
  );
278
281
  };
279
282
 
280
- export const ChatClient = ({ store, client }: { client?: ChatSDK; store: ChatStore }) => {
283
+ export const ChatClient = ({ store, client }: { client: ChatSDK; store: ChatStore }) => {
281
284
  return (
282
285
  <ChatStoreProvider store={store}>
283
- <ChatWidgets client={client} />
286
+ <TooltipProvider
287
+ zIndex={client?.options?.safeZIndex != null ? client.options.safeZIndex + 1 : 50}
288
+ >
289
+ <ChatWidgets client={client} />
290
+ </TooltipProvider>
284
291
  </ChatStoreProvider>
285
292
  );
286
293
  };
@@ -3,7 +3,7 @@ import { FieldType } from '@baishuyun/types';
3
3
  import { FontIcon } from '../bs-ui/font-icon';
4
4
  import { FormulasIcon } from '../bs-ui/bs-icons';
5
5
 
6
- export const FieldIcon = ({ type }: { type: FieldType & 'formula' }) => {
6
+ export const FieldIcon = ({ type }: { type: FieldType }) => {
7
7
  const code = FIELD_ICON_CODE_MAP[type] || 'unknown';
8
8
  if (type === 'formula') {
9
9
  return <FormulasIcon />;
@@ -19,7 +19,7 @@ import {
19
19
  PromptInputToolbar,
20
20
  PromptInputTools,
21
21
  } from './prompt-input';
22
- import { PreviewAttachment } from './preview-attachment';
22
+ import { AttachmentsPreviewer } from '@/components/bs-ui/attachments-previewer';
23
23
  import { ChatMessage, Attachment } from '@baishuyun/types';
24
24
  import { Button } from '@/components/ui/button';
25
25
  import { usePluginLifeCycleChainRunner } from '@/hooks/use-plugin-life-cycle-chain-runner';
@@ -221,6 +221,9 @@ function PureMultimodalInput({
221
221
  [setAttachments, uploadFile]
222
222
  );
223
223
 
224
+ const accept = useChatPreference()?.acceptAttachmentFileType?.join(',');
225
+ const hasAcceptableFileType = !!accept;
226
+
224
227
  // Add paste event listener to textarea
225
228
  useEffect(() => {
226
229
  const textarea = textareaRef.current;
@@ -241,7 +244,7 @@ function PureMultimodalInput({
241
244
  ref={fileInputRef}
242
245
  tabIndex={-1}
243
246
  type="file"
244
- // accept=".csv,text/csv,text/plain,.txt"
247
+ accept={accept}
245
248
  />
246
249
 
247
250
  <PromptInput
@@ -257,37 +260,29 @@ function PureMultimodalInput({
257
260
  }}
258
261
  >
259
262
  {(attachments.length > 0 || uploadQueue.length > 0) && (
260
- <div
261
- className="flex flex-row items-end gap-2 overflow-x-scroll"
262
- data-testid="attachments-preview"
263
- >
264
- {attachments.map((attachment) => (
265
- <PreviewAttachment
266
- attachment={attachment}
267
- key={attachment.url}
268
- onRemove={() => {
269
- setAttachments((currentAttachments) =>
270
- currentAttachments.filter((a) => a.url !== attachment.url)
271
- );
272
- if (fileInputRef.current) {
273
- fileInputRef.current.value = '';
274
- }
275
- }}
276
- />
277
- ))}
278
-
279
- {uploadQueue.map((filename) => (
280
- <PreviewAttachment
281
- attachment={{
282
- url: '',
283
- name: filename,
284
- contentType: '',
285
- }}
286
- isUploading={true}
287
- key={filename}
288
- />
289
- ))}
290
- </div>
263
+ <AttachmentsPreviewer
264
+ attachments={[
265
+ ...attachments.map((a) => ({
266
+ id: a.url,
267
+ url: a.url,
268
+ alt: a.name,
269
+ contentType: a.contentType,
270
+ })),
271
+ ...uploadQueue.map((filename) => ({
272
+ id: `uploading-${filename}`,
273
+ url: '',
274
+ alt: filename,
275
+ isLoading: true,
276
+ })),
277
+ ]}
278
+ onImageRemove={(item) => {
279
+ if (item.id.startsWith('uploading-')) return;
280
+ setAttachments((cur) => cur.filter((a) => a.url !== item.id));
281
+ if (fileInputRef.current) {
282
+ fileInputRef.current.value = '';
283
+ }
284
+ }}
285
+ />
291
286
  )}
292
287
  <div className="flex flex-row items-start gap-1 sm:gap-2">
293
288
  <PromptInputTextarea
@@ -306,7 +301,9 @@ function PureMultimodalInput({
306
301
  <PromptInputToolbar className="border-top-0! border-t-0! p-0 shadow-none dark:border-0 dark:border-transparent!">
307
302
  <div></div>
308
303
  <PromptInputTools className="gap-2.5">
309
- <AttachmentsButton fileInputRef={fileInputRef} status={status} />
304
+ {hasAcceptableFileType ? (
305
+ <AttachmentsButton fileInputRef={fileInputRef} status={status} />
306
+ ) : null}
310
307
  <ClearBtn setMessages={setMessages} status={status} />
311
308
  <DividerIcon />
312
309
  {status === 'submitted' || status === 'streaming' ? (
@@ -104,10 +104,7 @@ export const PromptInputToolbar = ({ className, ...props }: PromptInputToolbarPr
104
104
  export type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;
105
105
 
106
106
  export const PromptInputTools = ({ className, ...props }: PromptInputToolsProps) => (
107
- <div
108
- className={cn('flex items-center gap-1', '[&_button:first-child]:rounded-bl-xl', className)}
109
- {...props}
110
- />
107
+ <div className={cn('flex items-center gap-1', className)} {...props} />
111
108
  );
112
109
 
113
110
  export type PromptInputButtonProps = ComponentProps<typeof Button>;
@@ -11,7 +11,7 @@ export const OpeningLines = ({ sendMessage, setMessages }: OpeningLinesProps) =>
11
11
 
12
12
  return (
13
13
  <>
14
- 请问可以帮你创建什么表单?试试下面的建议吧!
14
+ 👋 你好,你想创建什么表单呢,不知道的话,可以试试下面的建议~
15
15
  <Suggestions
16
16
  onSelect={(title) => {
17
17
  sendMessage(
@@ -53,13 +53,20 @@ export const PreviewMessageWrapper = ({
53
53
  pointerEventsCls
54
54
  )}
55
55
  >
56
- <IconBtn icon={<CopyIcon />} onClick={onCopy} />
56
+ <IconBtn
57
+ tooltip="复制"
58
+ icon={<CopyIcon />}
59
+ onClick={onCopy}
60
+ className="!hover:bg-[#eff0f6]"
61
+ />
57
62
  {onGenerate ? (
58
63
  <IconBtn
64
+ tooltip="重新生成"
59
65
  icon={<RefreshIcon />}
60
66
  onClick={() => {
61
67
  onGenerate && onGenerate();
62
68
  }}
69
+ className="!hover:bg-[#eff0f6]"
63
70
  />
64
71
  ) : null}
65
72
  </div>
@@ -20,6 +20,8 @@ import { WarningMessage } from '../bs-ui/warning-msg';
20
20
  import { GenerateAnimation } from '../bs-ui/generate-animation';
21
21
  import { AttachmentPartGroup } from '../bs-ui/attachment-part-group';
22
22
  import { AttachmentPart } from '../bs-ui/attachment-part';
23
+ import { useChatStatus } from '@/hooks/use-frame-mode';
24
+ import { useEvtBus } from '@/hooks/use-evt-bus';
23
25
 
24
26
  const PurePreviewMessage = ({
25
27
  message,
@@ -45,6 +47,8 @@ const PurePreviewMessage = ({
45
47
  (p: { type: string; text?: string }) => p.type === 'text' && p.text?.trim()
46
48
  );
47
49
 
50
+ const chatStatus = useChatStatus();
51
+
48
52
  const hasFileType = message.parts?.some((p: any) => {
49
53
  if (Array.isArray(p)) {
50
54
  return p.some((subP) => subP?.type === 'file');
@@ -66,6 +70,8 @@ const PurePreviewMessage = ({
66
70
 
67
71
  const parts = onBeforePartsRender(fileGroupedParts);
68
72
 
73
+ const evtBus = useEvtBus();
74
+
69
75
  const { exec: onBeforeSendMsg } = usePluginLifeCycleChainRunner('onBeforeMessageSend');
70
76
 
71
77
  const handleCopy = () => {
@@ -75,7 +81,13 @@ const PurePreviewMessage = ({
75
81
  .join('\n');
76
82
 
77
83
  if (textParts) {
78
- navigator.clipboard.writeText(textParts);
84
+ try {
85
+ navigator.clipboard.writeText(textParts);
86
+ evtBus.emit('chat-msg-copied', {
87
+ messageId: message.id,
88
+ content: textParts,
89
+ });
90
+ } catch (err) {}
79
91
  }
80
92
  };
81
93
 
@@ -87,6 +99,10 @@ const PurePreviewMessage = ({
87
99
  });
88
100
  };
89
101
 
102
+ if (chatStatus.isHide) {
103
+ return null;
104
+ }
105
+
90
106
  return (
91
107
  <PreviewMessageWrapper
92
108
  role={message.role}