@baishuyun/chat-sdk 0.0.17 → 0.0.18
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/.turbo/turbo-build.log +178 -115
- package/CHANGELOG.md +8 -0
- package/dist/chat-sdk.js +26425 -34262
- package/dist/chat-sdk.js.map +1 -1
- package/dist/chat-sdk.umd.cjs +219 -296
- package/dist/chat-sdk.umd.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/package.json +4 -4
- package/src/chat.tsx +4 -1
- package/src/components/biz-comp/chat-client.tsx +1 -1
- package/src/components/biz-comp/multi-modal-input/index.tsx +22 -5
- package/src/components/biz-comp/preview-message.tsx +25 -3
- package/src/components/bs-ui/attachment-part-group.tsx +5 -2
- package/src/components/bs-ui/attachment-part.tsx +14 -9
- package/src/components/bs-ui/attachments-previewer.tsx +2 -2
- package/src/components/bs-ui/base-button.tsx +14 -4
- package/src/components/bs-ui/border-animation.tsx +107 -0
- package/src/components/bs-ui/bs-icons.tsx +6 -0
- package/src/components/bs-ui/confirm-dialog.tsx +17 -11
- package/src/components/bs-ui/fields-previewer.tsx +16 -8
- package/src/components/bs-ui/img-part.tsx +4 -2
- package/src/components/bs-ui/previewer-header.tsx +26 -7
- package/src/components/bs-ui/primary-entry-btn.tsx +2 -1
- package/src/index.tsx +0 -1
- package/src/lib/utils.ts +60 -2
- package/src/plugins/form-builder-plugin/components/create-form-confirm.tsx +0 -2
- package/src/plugins/form-builder-plugin/components/design-doc-part.tsx +19 -0
- package/src/plugins/form-builder-plugin/components/fields-part.tsx +78 -0
- package/src/plugins/form-builder-plugin/components/msg-part.tsx +20 -165
- package/src/plugins/form-builder-plugin/components/suggestion-part.tsx +62 -0
- package/src/plugins/form-builder-plugin/hooks/index.tsx +50 -0
- package/src/plugins/form-builder-plugin/index.ts +39 -30
- package/src/plugins/form-builder-plugin/types.ts +19 -2
- package/src/plugins/form-builder-plugin/utils/get-render-strategy.ts +27 -0
- package/src/plugins/form-builder-plugin/utils/index.ts +44 -37
- package/src/plugins/form-filling-plugin/components/FormFillingOpeningLines.tsx +4 -0
- package/src/plugins/form-filling-plugin/components/batch-fill-part.tsx +17 -0
- package/src/plugins/form-filling-plugin/components/batch-generator-action.tsx +6 -1
- package/src/plugins/form-filling-plugin/components/entry-btn.tsx +1 -1
- package/src/plugins/form-filling-plugin/components/first-batch-generating-animation.tsx +6 -16
- package/src/plugins/form-filling-plugin/components/msg-part.tsx +39 -122
- package/src/plugins/form-filling-plugin/components/non-first-batch-generating-animation.tsx +9 -19
- package/src/plugins/form-filling-plugin/components/single-fill-part.tsx +42 -0
- package/src/plugins/form-filling-plugin/hooks/use-conversation-id-in-ctx.ts +13 -0
- package/src/plugins/form-filling-plugin/hooks/use-fields-data.ts +110 -0
- package/src/plugins/form-filling-plugin/index.ts +49 -43
- package/src/plugins/form-filling-plugin/types.ts +19 -1
- package/src/plugins/report-query-plugin/components/query-msg-part.tsx +13 -4
- package/src/plugins/report-query-plugin/index.ts +20 -11
- package/src/store/index.ts +0 -1
- package/src/stories/BorderAnimation.stories.tsx +116 -0
- package/src/stories/PreviewerHeader.stories.tsx +24 -0
- package/src/style.css +25 -0
- package/src/plugins/form-builder-plugin/hooks/index.ts +0 -0
- package/src/plugins/general-model-form-builder-plugin/components/confirmer.tsx +0 -90
- package/src/plugins/general-model-form-builder-plugin/components/ghost-evt-dispatcher.tsx +0 -69
- package/src/plugins/general-model-form-builder-plugin/components/msg-part.tsx +0 -147
- package/src/plugins/general-model-form-builder-plugin/components/new-confirmer.tsx +0 -191
- package/src/plugins/general-model-form-builder-plugin/const.ts +0 -3
- package/src/plugins/general-model-form-builder-plugin/index.ts +0 -20
- package/src/plugins/general-model-form-builder-plugin/types.ts +0 -1
|
@@ -270,14 +270,14 @@ export const AttachmentsPreviewer = ({
|
|
|
270
270
|
)}
|
|
271
271
|
|
|
272
272
|
{attachment.isLoading && (
|
|
273
|
-
<div className="absolute inset-0 flex items-center justify-center rounded-[10px] bg-black/50">
|
|
273
|
+
<div className="absolute inset-0 flex items-center justify-center rounded-[10px] bg-black/50 pointer-events-none">
|
|
274
274
|
<div className="inline-flex animate-spin items-center justify-center">
|
|
275
275
|
<LoaderIcon size={16} />
|
|
276
276
|
</div>
|
|
277
277
|
</div>
|
|
278
278
|
)}
|
|
279
279
|
|
|
280
|
-
{onImageRemove && !attachment.isLoading && (
|
|
280
|
+
{onImageRemove /* && !attachment.isLoading*/ && (
|
|
281
281
|
<button
|
|
282
282
|
className={cn(
|
|
283
283
|
'absolute -top-[6px] -right-[6px]',
|
|
@@ -2,21 +2,23 @@ import { forwardRef } from 'react';
|
|
|
2
2
|
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
import { cn } from '@/lib/utils';
|
|
4
4
|
import { transCls } from '@/const/ui';
|
|
5
|
+
import { BsTooltip } from './tooltip';
|
|
5
6
|
|
|
6
7
|
export interface BaseButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
7
8
|
icon?: ReactNode;
|
|
9
|
+
tooltip?: string;
|
|
8
10
|
hoverTextCls?: string;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export const BaseButton = forwardRef<HTMLButtonElement, BaseButtonProps>(
|
|
12
|
-
({ className, children, hoverTextCls, icon, type, ...rest }, ref) => {
|
|
13
|
-
|
|
14
|
+
({ className, children, hoverTextCls, tooltip, icon, type, ...rest }, ref) => {
|
|
15
|
+
const btn = (
|
|
14
16
|
<button
|
|
15
17
|
ref={ref}
|
|
16
18
|
type={type ?? 'button'}
|
|
17
19
|
className={cn(
|
|
18
20
|
'group inline-flex items-center gap-[6px] px-[16px] rounded-[4px] h-[34px]',
|
|
19
|
-
'border border-[#E0E0E0] bg-transparent',
|
|
21
|
+
'border border-solid border-[#E0E0E0] bg-transparent',
|
|
20
22
|
'text-[14px] font-[400] leading-[normal] text-[#030303]',
|
|
21
23
|
'hover:bg-[#EFF0F6] hover:border-transparent',
|
|
22
24
|
'cursor-pointer',
|
|
@@ -30,9 +32,17 @@ export const BaseButton = forwardRef<HTMLButtonElement, BaseButtonProps>(
|
|
|
30
32
|
{...rest}
|
|
31
33
|
>
|
|
32
34
|
{icon ? <span className="shrink-0">{icon}</span> : null}
|
|
33
|
-
|
|
35
|
+
{children ? (
|
|
36
|
+
<span className="whitespace-nowrap">{children}</span>
|
|
37
|
+
) : null}
|
|
34
38
|
</button>
|
|
35
39
|
);
|
|
40
|
+
|
|
41
|
+
if (tooltip) {
|
|
42
|
+
return <BsTooltip content={tooltip}>{btn}</BsTooltip>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return btn;
|
|
36
46
|
}
|
|
37
47
|
);
|
|
38
48
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { cn } from '@/lib/utils';
|
|
3
|
+
|
|
4
|
+
export interface BorderAnimationProps {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
className?: string;
|
|
7
|
+
/** Duration of one full animation cycle in seconds @default 2 */
|
|
8
|
+
duration?: number;
|
|
9
|
+
/** Border radius in pixels @default 4 */
|
|
10
|
+
borderRadius?: number;
|
|
11
|
+
/** Width of the animated beam in pixels @default 30 */
|
|
12
|
+
beamWidth?: number;
|
|
13
|
+
/** Height of the animated beam in pixels @default 4 */
|
|
14
|
+
beamHeight?: number;
|
|
15
|
+
/** Gradient for the animated beam */
|
|
16
|
+
gradient?: string;
|
|
17
|
+
height?: string | number;
|
|
18
|
+
/** Border width in pixels @default 1.5 */
|
|
19
|
+
borderWidth?: number;
|
|
20
|
+
/** Default border color @default '#E5E5E5' */
|
|
21
|
+
borderColor?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const defaultGradient = `linear-gradient(90deg,
|
|
25
|
+
rgba(132, 180, 255, 0) 0%,
|
|
26
|
+
rgba(132, 180, 255, 0.5) 10%,
|
|
27
|
+
rgba(132, 180, 255, 1) 25%,
|
|
28
|
+
rgb(2, 101, 255) 50%,
|
|
29
|
+
rgba(177, 250, 255, 1) 75%,
|
|
30
|
+
rgba(177, 250, 255, 0.5) 90%,
|
|
31
|
+
rgba(177, 250, 255, 0) 100%
|
|
32
|
+
)`;
|
|
33
|
+
|
|
34
|
+
export const BorderAnimation: React.FC<BorderAnimationProps> = ({
|
|
35
|
+
children,
|
|
36
|
+
className,
|
|
37
|
+
duration = 2,
|
|
38
|
+
borderRadius = 4,
|
|
39
|
+
beamWidth = 170,
|
|
40
|
+
beamHeight = 4,
|
|
41
|
+
// gradient = 'linear-gradient(180deg, #84B4FF 0%, #0265FF 50%, #B1FAFF 100%)',
|
|
42
|
+
// gradient = 'linear-gradient(90deg, rgb(132, 180, 255) 0%, rgb(2, 101, 255) 50%, rgb(177, 250, 255) 100%)',
|
|
43
|
+
gradient = defaultGradient,
|
|
44
|
+
borderWidth = 1,
|
|
45
|
+
height,
|
|
46
|
+
borderColor = 'transparent',
|
|
47
|
+
}) => {
|
|
48
|
+
const pseudoCls = "gradient-border"
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
className={cn('relative w-full overflow-hidden', className, pseudoCls)}
|
|
52
|
+
style={{
|
|
53
|
+
borderRadius: `${borderRadius}px`,
|
|
54
|
+
border: `${borderWidth}px solid ${borderColor}`,
|
|
55
|
+
...(height ? { height } : {}),
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
{children}
|
|
59
|
+
{/* Animated border overlay */}
|
|
60
|
+
<div
|
|
61
|
+
style={{
|
|
62
|
+
position: 'absolute',
|
|
63
|
+
// inset: `${-borderWidth}px`,
|
|
64
|
+
inset: 0,
|
|
65
|
+
pointerEvents: 'none',
|
|
66
|
+
border: `${borderWidth}px solid transparent`,
|
|
67
|
+
borderRadius: 'inherit',
|
|
68
|
+
// contain: 'layout style paint',
|
|
69
|
+
maskImage:
|
|
70
|
+
'linear-gradient(transparent, transparent), linear-gradient(white, white)',
|
|
71
|
+
WebkitMaskImage:
|
|
72
|
+
'linear-gradient(transparent, transparent), linear-gradient(white, white)',
|
|
73
|
+
maskOrigin: 'border-box, border-box',
|
|
74
|
+
WebkitMaskOrigin: 'border-box, border-box',
|
|
75
|
+
maskClip: 'padding-box, border-box',
|
|
76
|
+
WebkitMaskClip: 'padding-box, border-box',
|
|
77
|
+
maskComposite: 'intersect',
|
|
78
|
+
WebkitMaskComposite: 'source-in',
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
{/* Traveling beam */}
|
|
82
|
+
<div
|
|
83
|
+
style={{
|
|
84
|
+
position: 'absolute',
|
|
85
|
+
aspectRatio: '1 / 1',
|
|
86
|
+
width: `${beamWidth}px`,
|
|
87
|
+
height: `${beamHeight}px`,
|
|
88
|
+
background: gradient,
|
|
89
|
+
offsetPath: `rect(0px auto auto 0px round ${borderRadius}px)`,
|
|
90
|
+
animation: `bs-border-travel ${duration}s linear infinite`,
|
|
91
|
+
willChange: 'offset-distance',
|
|
92
|
+
transform: 'translateZ(0)',
|
|
93
|
+
backfaceVisibility: 'hidden',
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
<style>{`
|
|
98
|
+
@keyframes bs-border-travel {
|
|
99
|
+
0% { offset-distance: 0%; }
|
|
100
|
+
100% { offset-distance: 100%; }
|
|
101
|
+
}
|
|
102
|
+
`}</style>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export default BorderAnimation;
|
|
@@ -1094,3 +1094,9 @@ export const DataReportIcon = () => (
|
|
|
1094
1094
|
/>
|
|
1095
1095
|
</svg>
|
|
1096
1096
|
);
|
|
1097
|
+
|
|
1098
|
+
export const BrushIcon = () => (
|
|
1099
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 15 15" fill="currentColor">
|
|
1100
|
+
<path d="M14.72 2.64C14.72 2.16 14.56 1.76 14.24 1.44L13.28 0.48C12.64 -0.16 11.6 -0.16 10.96 0.48L8.8 2.64L7.04 0.96C6.4 0.32 5.36 0.32 4.72 0.96L3.12 2.56L0.48 5.28C0.16 5.6 0 6 0 6.4C0 6.8 0.16 7.28 0.48 7.6L2 9.12L7.12 14.24C7.44 14.56 7.84 14.72 8.24 14.72H8.32C8.72 14.72 9.2 14.56 9.52 14.24L12.08 11.68C12.08 11.68 12.16 11.68 12.16 11.6C12.16 11.6 12.16 11.52 12.24 11.52L13.76 10C14.08 9.68 14.24 9.28 14.24 8.8C14.24 8.32 14.08 7.92 13.76 7.6L12.08 5.92L14.24 3.76C14.48 3.44 14.72 3.04 14.72 2.64ZM8.64 13.44C8.56 13.52 8.4 13.6 8.24 13.6H8.16C8 13.6 7.92 13.52 7.76 13.44L6.8 12.48L7.6 11.68C7.84 11.44 7.84 11.12 7.6 10.88C7.36 10.64 7.04 10.64 6.8 10.88L6 11.68L4.96 10.64L6.24 9.36C6.48 9.12 6.48 8.8 6.24 8.56C6 8.32 5.68 8.32 5.44 8.56L4.16 9.84L2.96 8.64L3.6 8C3.84 7.76 3.84 7.44 3.6 7.2C3.36 6.96 3.04 6.96 2.8 7.2L2.16 7.84L1.2 6.8C1.12 6.72 1.12 6.56 1.12 6.4C1.12 6.24 1.2 6.16 1.28 6L3.52 3.76L10.96 11.2L8.64 13.44ZM13.44 2.96L10.88 5.52C10.64 5.76 10.64 6.08 10.88 6.32L12.96 8.4C13.04 8.48 13.12 8.64 13.12 8.8C13.12 8.96 13.04 9.04 12.96 9.2L11.68 10.4L4.32 2.96L5.52 1.76C5.68 1.6 6.08 1.6 6.24 1.76L8.32 3.84C8.56 4.08 8.88 4.08 9.12 3.84L11.68 1.28C11.84 1.12 12.24 1.12 12.4 1.28L13.36 2.24C13.44 2.32 13.52 2.48 13.52 2.64C13.52 2.8 13.52 2.88 13.44 2.96Z"/>
|
|
1101
|
+
</svg>
|
|
1102
|
+
)
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '@/components/ui/dialog';
|
|
9
9
|
import { QuestionIcon } from './bs-icons';
|
|
10
10
|
import { cn } from '@/lib/utils';
|
|
11
|
+
import { BaseButton } from './base-button';
|
|
11
12
|
|
|
12
13
|
export interface ConfirmDialogProps {
|
|
13
14
|
open?: boolean;
|
|
@@ -84,7 +85,6 @@ export function ConfirmDialog({
|
|
|
84
85
|
{showLink ? (
|
|
85
86
|
<button
|
|
86
87
|
type="button"
|
|
87
|
-
onClick={onLinkClick}
|
|
88
88
|
className="text-[14px] text-[#0265ff] hover:text-[#0265ff]/80 bg-transparent border-none cursor-pointer"
|
|
89
89
|
>
|
|
90
90
|
{linkText}
|
|
@@ -95,25 +95,31 @@ export function ConfirmDialog({
|
|
|
95
95
|
|
|
96
96
|
<div className="flex items-center gap-3">
|
|
97
97
|
{showCancel && (
|
|
98
|
-
<
|
|
99
|
-
|
|
98
|
+
<BaseButton
|
|
99
|
+
className="hover:text-[#121111]"
|
|
100
100
|
onClick={onCancel}
|
|
101
|
-
style={btnStyle}
|
|
102
|
-
className={cn(
|
|
103
|
-
'h-8 px-5 py-1.5 rounded border border-[#e0e0e0]',
|
|
104
|
-
'text-[14px] text-[#030303] bg-white',
|
|
105
|
-
'hover:bg-gray-50 cursor-pointer'
|
|
106
|
-
)}
|
|
107
101
|
>
|
|
108
102
|
{cancelText}
|
|
109
|
-
</
|
|
103
|
+
</BaseButton>
|
|
104
|
+
// <button
|
|
105
|
+
// type="button"
|
|
106
|
+
// onClick={onCancel}
|
|
107
|
+
// style={btnStyle}
|
|
108
|
+
// className={cn(
|
|
109
|
+
// 'h-8 px-5 py-1.5 rounded border border-[#e0e0e0]',
|
|
110
|
+
// 'text-[14px] text-[#030303] bg-white',
|
|
111
|
+
// 'hover:bg-gray-50 cursor-pointer'
|
|
112
|
+
// )}
|
|
113
|
+
// >
|
|
114
|
+
// {cancelText}
|
|
115
|
+
// </button>
|
|
110
116
|
)}
|
|
111
117
|
<button
|
|
112
118
|
type="button"
|
|
113
119
|
onClick={onConfirm}
|
|
114
120
|
style={btnStyle}
|
|
115
121
|
className={cn(
|
|
116
|
-
'h-8 px-5 py-1.5 rounded',
|
|
122
|
+
// 'h-8 px-5 py-1.5 rounded',
|
|
117
123
|
'text-[14px] text-white bg-[#0265ff]',
|
|
118
124
|
'hover:bg-[#0265ff]/80 cursor-pointer border-none'
|
|
119
125
|
)}
|
|
@@ -5,6 +5,7 @@ import { BorderColorAnimation } from './border-color-animation';
|
|
|
5
5
|
import { FieldsGeneratingIndicator } from './fields-generating-indicator';
|
|
6
6
|
import { PreviewerHeader, PreviewerHeaderProps } from './previewer-header';
|
|
7
7
|
import { useShadow } from '@/hooks/use-shadow';
|
|
8
|
+
import { TooltipProvider } from '../ui/tooltip';
|
|
8
9
|
|
|
9
10
|
export interface FieldsPreviewerProps extends PreviewerHeaderProps {
|
|
10
11
|
fullscreen?: boolean;
|
|
@@ -67,6 +68,7 @@ export const FieldsPreviewer = forwardRef<HTMLDivElement, FieldsPreviewerProps>(
|
|
|
67
68
|
variant = 'fields-previewer',
|
|
68
69
|
onSave,
|
|
69
70
|
onCancel,
|
|
71
|
+
onClear,
|
|
70
72
|
style,
|
|
71
73
|
confirmContent,
|
|
72
74
|
fullContent,
|
|
@@ -139,6 +141,7 @@ export const FieldsPreviewer = forwardRef<HTMLDivElement, FieldsPreviewerProps>(
|
|
|
139
141
|
closeBtnLabel={closeBtnLabel}
|
|
140
142
|
disableOperation={disableOperation}
|
|
141
143
|
disableConfirm={disableConfirm}
|
|
144
|
+
onClear={onClear}
|
|
142
145
|
/>
|
|
143
146
|
<div className={contentCls} style={fullContentStyle}>
|
|
144
147
|
{empty && isFields ? <PlaceholderDashedBox /> : children}
|
|
@@ -187,16 +190,21 @@ export const FieldsPreviewer = forwardRef<HTMLDivElement, FieldsPreviewerProps>(
|
|
|
187
190
|
);
|
|
188
191
|
|
|
189
192
|
return (
|
|
190
|
-
<
|
|
193
|
+
<TooltipProvider
|
|
194
|
+
delayDuration={100}
|
|
191
195
|
zIndex={zIndex}
|
|
192
|
-
padding={isFields ? p : 0}
|
|
193
|
-
disableGradient={fullscreen || !isFields}
|
|
194
|
-
bgColor={isFields ? '#fff' : '#F5F7FA'}
|
|
195
|
-
ref={setAbsContainer}
|
|
196
196
|
>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
197
|
+
<AbsFullscreenGradientBg
|
|
198
|
+
zIndex={zIndex}
|
|
199
|
+
padding={isFields ? p : 0}
|
|
200
|
+
disableGradient={fullscreen || !isFields}
|
|
201
|
+
bgColor={isFields ? '#fff' : '#F5F7FA'}
|
|
202
|
+
ref={setAbsContainer}
|
|
203
|
+
>
|
|
204
|
+
{fullscreen ? contentJsx : content}
|
|
205
|
+
{rightArea}
|
|
206
|
+
</AbsFullscreenGradientBg>
|
|
207
|
+
</TooltipProvider>
|
|
200
208
|
);
|
|
201
209
|
}
|
|
202
210
|
);
|
|
@@ -6,13 +6,15 @@ export interface ImgMsgPartProps {
|
|
|
6
6
|
className?: string;
|
|
7
7
|
/** Size in pixels (width & height), defaults to 102 */
|
|
8
8
|
size?: number;
|
|
9
|
+
onClick?: () => void;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
export const ImgMsgPart = ({ url, alt = '', className, size = 102 }: ImgMsgPartProps) => {
|
|
12
|
+
export const ImgMsgPart = ({ url, alt = '', className, size = 102, onClick }: ImgMsgPartProps) => {
|
|
12
13
|
return (
|
|
13
14
|
<div
|
|
14
|
-
className={cn('relative overflow-hidden rounded-[10px] border border-[#e0e0e0]', className)}
|
|
15
|
+
className={cn('relative overflow-hidden rounded-[10px] border border-[#e0e0e0] hover:border-[#0265FF]/20 hover:cursor-pointer', onClick && 'cursor-pointer', className)}
|
|
15
16
|
style={{ width: size, height: size }}
|
|
17
|
+
onClick={onClick}
|
|
16
18
|
>
|
|
17
19
|
<img
|
|
18
20
|
src={url}
|
|
@@ -5,6 +5,9 @@ import { BackIcon, InfoIcon } from './bs-icons';
|
|
|
5
5
|
import { LinearGradientColorBgAnimation } from './linear-gradient-color-bg-animation';
|
|
6
6
|
import { ConfirmDialog } from './confirm-dialog';
|
|
7
7
|
import { Spinner } from '../ui/spinner';
|
|
8
|
+
import { BaseButton } from './base-button';
|
|
9
|
+
import { BrushIcon } from './bs-icons';
|
|
10
|
+
import { cn } from '@/lib/utils';
|
|
8
11
|
|
|
9
12
|
export interface PreviewerHeaderProps {
|
|
10
13
|
title: string;
|
|
@@ -14,6 +17,7 @@ export interface PreviewerHeaderProps {
|
|
|
14
17
|
onBackClick?: () => void;
|
|
15
18
|
onSave?: (isConfirm?: boolean) => void;
|
|
16
19
|
onCancel?: () => void;
|
|
20
|
+
onClear?: () => void;
|
|
17
21
|
style?: React.CSSProperties;
|
|
18
22
|
|
|
19
23
|
confirmTitle?: string;
|
|
@@ -25,6 +29,7 @@ export interface PreviewerHeaderProps {
|
|
|
25
29
|
disableConfirm?: boolean;
|
|
26
30
|
|
|
27
31
|
disableOperation?: boolean;
|
|
32
|
+
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
export const PreviewerHeader = ({
|
|
@@ -39,6 +44,7 @@ export const PreviewerHeader = ({
|
|
|
39
44
|
onSave,
|
|
40
45
|
closeBtnLabel,
|
|
41
46
|
onCancel,
|
|
47
|
+
onClear,
|
|
42
48
|
variant = 'fields-previewer',
|
|
43
49
|
dialogContainer,
|
|
44
50
|
disableConfirm,
|
|
@@ -67,9 +73,9 @@ export const PreviewerHeader = ({
|
|
|
67
73
|
<IconBtn
|
|
68
74
|
icon={<BackIcon />}
|
|
69
75
|
onClick={() => {
|
|
70
|
-
if (disableOperation) {
|
|
71
|
-
|
|
72
|
-
}
|
|
76
|
+
// if (disableOperation) {
|
|
77
|
+
// return;
|
|
78
|
+
// }
|
|
73
79
|
|
|
74
80
|
if (disableConfirm) {
|
|
75
81
|
onCancel?.();
|
|
@@ -91,6 +97,19 @@ export const PreviewerHeader = ({
|
|
|
91
97
|
)}
|
|
92
98
|
</div>
|
|
93
99
|
<div className="flex items-center gap-[10px]">
|
|
100
|
+
{onClear && (
|
|
101
|
+
<BaseButton
|
|
102
|
+
className={cn('w-[34px] h-[34px] text-[#8d8d8d] p-0 flex items-center justify-center hover:border-[#e0e0e0] hover:text-[#8d8d8d] border border-solid border-[#e0e0e0] ', {
|
|
103
|
+
'pointer-events-none opacity-50': disableOperation || loading,
|
|
104
|
+
})}
|
|
105
|
+
tooltip='清除全部'
|
|
106
|
+
icon={<BrushIcon />}
|
|
107
|
+
onClick={() => {
|
|
108
|
+
onClear?.();
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
)}
|
|
112
|
+
|
|
94
113
|
{onSave && (
|
|
95
114
|
<button
|
|
96
115
|
onClick={async () => {
|
|
@@ -120,9 +139,9 @@ export const PreviewerHeader = ({
|
|
|
120
139
|
{onCancel && (
|
|
121
140
|
<button
|
|
122
141
|
onClick={() => {
|
|
123
|
-
if (disableOperation) {
|
|
124
|
-
|
|
125
|
-
}
|
|
142
|
+
// if (disableOperation) {
|
|
143
|
+
// return;
|
|
144
|
+
// }
|
|
126
145
|
|
|
127
146
|
if (disableConfirm) {
|
|
128
147
|
onCancel?.();
|
|
@@ -136,7 +155,7 @@ export const PreviewerHeader = ({
|
|
|
136
155
|
btnResetStyle,
|
|
137
156
|
'text-[#121111] text-[14px] bg-[#fff] border border-solid border-[#e0e0e0] hover:bg-[#f0f0f0]',
|
|
138
157
|
{
|
|
139
|
-
'pointer-events-none opacity-50':
|
|
158
|
+
'pointer-events-none opacity-50': loading,
|
|
140
159
|
}
|
|
141
160
|
)}
|
|
142
161
|
>
|
|
@@ -6,7 +6,7 @@ import { transCls } from '@/const/ui';
|
|
|
6
6
|
|
|
7
7
|
export interface PrimaryEntryBtnProps extends ButtonProps {
|
|
8
8
|
minimal?: boolean;
|
|
9
|
-
entryVariant?: 'default' | 'ghost' | 'selected';
|
|
9
|
+
entryVariant?: 'default' | 'ghost' | 'selected' | 'light';
|
|
10
10
|
icon?: ReactNode;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -23,6 +23,7 @@ export const PrimaryEntryBtn = ({
|
|
|
23
23
|
default:
|
|
24
24
|
"relative overflow-hidden bg-[linear-gradient(180deg,#DBE9FF_0%,#E9FFFD_100%)] h-[30px] hover:text-white before:content-[''] before:absolute before:inset-0 before:z-0 before:bg-[linear-gradient(180deg,#267AFF_0%,#B2EEFF_100%)] before:opacity-0 hover:before:opacity-100 before:transition-opacity before:duration-200 before:ease-out font-[500] text-[#0265ff]",
|
|
25
25
|
ghost: 'bg-transparent text-[#030303] font-[400] px-[10px] hover:bg-[#EFF0F6] h-[34px]',
|
|
26
|
+
light: 'text-[#030303] font-[400] px-[10px] bg-[#F0F6FF] hover:bg-[#d8dde5] h-[34px]',
|
|
26
27
|
selected: 'bg-[#EFF0F6] text-[#030303] font-[400] px-[10px]',
|
|
27
28
|
};
|
|
28
29
|
|
package/src/index.tsx
CHANGED
package/src/lib/utils.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { clsx, type ClassValue } from 'clsx';
|
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
3
|
import { UIDataTypes, UIMessagePart, UITools } from 'ai';
|
|
4
4
|
import { Field } from '@/plugins/form-builder-base-plugin/types';
|
|
5
|
-
import { IQueryResult } from '@baishuyun/types';
|
|
5
|
+
import { IFilledField, IQueryResult } from '@baishuyun/types';
|
|
6
6
|
import { FORM_ICON_OPTIONS } from '@/const/ui';
|
|
7
7
|
|
|
8
8
|
export function cn(...inputs: ClassValue[]) {
|
|
@@ -117,7 +117,7 @@ export const GetPartErrMsg = (part: any): string => {
|
|
|
117
117
|
return payload?.error || '未知错误';
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
-
export const GetFieldJsonInfo = (part: any):
|
|
120
|
+
export const GetFieldJsonInfo = (part: any): IFilledField | null => {
|
|
121
121
|
const payload = extractTextPartMeta(part);
|
|
122
122
|
if (payload?.type !== 'mcp-fields-json') {
|
|
123
123
|
return null;
|
|
@@ -326,3 +326,61 @@ export const getIconOption = (iconValue?: number) => {
|
|
|
326
326
|
|
|
327
327
|
return option || options[8];
|
|
328
328
|
};
|
|
329
|
+
|
|
330
|
+
export const ensureEndSlash = (url: string): string => {
|
|
331
|
+
if (!url) {
|
|
332
|
+
return '';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return url.endsWith('/') ? url : `${url}/`;
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
export const generatePhoneFromTimestamp = (): string => {
|
|
339
|
+
const timestamp = Date.now().toString();
|
|
340
|
+
// 第二位取 3-9 的随机数,避免过于规律
|
|
341
|
+
const secondDigit = 3 + Math.floor(Math.random() * 7);
|
|
342
|
+
// 取时间戳后9位,如果不足则前面补零
|
|
343
|
+
const suffix = timestamp.slice(-9).padStart(9, '0');
|
|
344
|
+
return `1${secondDigit}${suffix}`;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
export const shortTimeHash = (length = 4) => {
|
|
348
|
+
const time = Date.now().toString(36); // 时间转36进制
|
|
349
|
+
const random = Math.random().toString(36).slice(2, 4); // 防碰撞
|
|
350
|
+
const combined = time + random;
|
|
351
|
+
|
|
352
|
+
// 进一步混淆
|
|
353
|
+
let hash = '';
|
|
354
|
+
for (let i = 0; i < combined.length && hash.length < length; i++) {
|
|
355
|
+
hash += combined.charCodeAt(i).toString(36);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return hash.slice(0, length);
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
export const generateUniqueValue = (numOnly?: boolean) => {
|
|
362
|
+
if (numOnly) {
|
|
363
|
+
return generatePhoneFromTimestamp();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return shortTimeHash(4);
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Convert an image URL to a base64 data URL via canvas.
|
|
371
|
+
* Returns the original URL on failure (e.g. CORS).
|
|
372
|
+
*/
|
|
373
|
+
export const imageUrlToBase64 = (url: string, mediaType = 'image/png'): Promise<string> =>
|
|
374
|
+
new Promise((resolve) => {
|
|
375
|
+
const img = new Image();
|
|
376
|
+
img.crossOrigin = 'anonymous';
|
|
377
|
+
img.onload = () => {
|
|
378
|
+
const canvas = document.createElement('canvas');
|
|
379
|
+
canvas.width = img.naturalWidth;
|
|
380
|
+
canvas.height = img.naturalHeight;
|
|
381
|
+
canvas.getContext('2d')!.drawImage(img, 0, 0);
|
|
382
|
+
resolve(canvas.toDataURL(mediaType));
|
|
383
|
+
};
|
|
384
|
+
img.onerror = () => resolve(url);
|
|
385
|
+
img.src = url;
|
|
386
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { MsgPartCompProps } from '@baishuyun/types';
|
|
2
|
+
import { IExtraPartProps } from '../types';
|
|
3
|
+
import { DesignInfo } from './design-info';
|
|
4
|
+
import { CreateFormConfirm } from './create-form-confirm';
|
|
5
|
+
|
|
6
|
+
export const DesignDocPart: React.FC<MsgPartCompProps & IExtraPartProps> = (props) => {
|
|
7
|
+
const { part, sendMessage } = props;
|
|
8
|
+
const appendConfirmBtn =
|
|
9
|
+
// TODO: 特殊文本解析在 node 端处理,前端只负责渲染
|
|
10
|
+
part.text?.includes('【确认搭建');
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<DesignInfo designMarkdown={part.text} id={props.id}>
|
|
14
|
+
{appendConfirmBtn ? (
|
|
15
|
+
<CreateFormConfirm sendMessage={sendMessage} designDoc={part.text} />
|
|
16
|
+
) : null}
|
|
17
|
+
</DesignInfo>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { MessageContent } from '@/components/biz-comp/message-content';
|
|
2
|
+
import { MsgPartCompProps, RealFormInfo } from '@baishuyun/types';
|
|
3
|
+
import { useBuildFieldsData } from '../hooks';
|
|
4
|
+
import { IExtraPartProps } from '../types';
|
|
5
|
+
import { useRef } from 'react';
|
|
6
|
+
import {
|
|
7
|
+
FieldCheckerListMsg,
|
|
8
|
+
FieldCheckerListMsgRef,
|
|
9
|
+
} from '@/components/biz-comp/FieldCheckerListMsg';
|
|
10
|
+
import { TextUIPart } from 'ai';
|
|
11
|
+
import { FormBuilderPlugin } from '..';
|
|
12
|
+
import { MCP_PLUGIN_NAME } from '../const';
|
|
13
|
+
import { usePlugin } from '@/hooks/use-plugin';
|
|
14
|
+
import { PrimaryConfirmBtn } from '@/components/bs-ui/primary-confirm-btn';
|
|
15
|
+
import { CheckerIcon } from '@/components/bs-ui/bs-icons';
|
|
16
|
+
import { useEvtBus } from '@/hooks/use-evt-bus';
|
|
17
|
+
|
|
18
|
+
export const FieldsPart: React.FC<MsgPartCompProps & IExtraPartProps> = (props) => {
|
|
19
|
+
const { confirmed: fieldsConfirmed, part, status } = props;
|
|
20
|
+
const p = part as TextUIPart;
|
|
21
|
+
const fieldPropsArray = useBuildFieldsData(p, !!fieldsConfirmed);
|
|
22
|
+
const checkerRef = useRef<FieldCheckerListMsgRef>(null);
|
|
23
|
+
const plugin = usePlugin<FormBuilderPlugin>(MCP_PLUGIN_NAME);
|
|
24
|
+
const formName = plugin?.getCurrentWorkingForm()?.formName || '';
|
|
25
|
+
const evtBus = useEvtBus();
|
|
26
|
+
|
|
27
|
+
const fieldsPrefix = (
|
|
28
|
+
<div>
|
|
29
|
+
<b style={{ display: 'inline-block' }}>{formName}</b>
|
|
30
|
+
{` 已生成${fieldPropsArray.length}个字段`}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const confirmedLabel = fieldsConfirmed ? (
|
|
35
|
+
<>
|
|
36
|
+
<CheckerIcon className="text-[#0265ff]" /> 已保存{' '}
|
|
37
|
+
{checkerRef.current?.getCheckedFieldsLength() ?? 0} 个字段
|
|
38
|
+
</>
|
|
39
|
+
) : (
|
|
40
|
+
'保存字段'
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const fieldsSuffix =
|
|
44
|
+
status === 'ready'
|
|
45
|
+
? '请选择需要的字段添加到表单。您可以补充描述后重新生成,表单中已添加字段在下次生成时保留。'
|
|
46
|
+
: null;
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<MessageContent className="gap-0 py-0" compact>
|
|
50
|
+
{fieldsPrefix}
|
|
51
|
+
<FieldCheckerListMsg
|
|
52
|
+
ref={checkerRef}
|
|
53
|
+
fields={fieldPropsArray}
|
|
54
|
+
streaming={status === 'streaming'}
|
|
55
|
+
confirmed={fieldsConfirmed}
|
|
56
|
+
>
|
|
57
|
+
{status === 'ready' ? (
|
|
58
|
+
<PrimaryConfirmBtn
|
|
59
|
+
className="mt-[20px] mb-[10px]"
|
|
60
|
+
disabledWithInfo={fieldsConfirmed}
|
|
61
|
+
onClick={() => {
|
|
62
|
+
evtBus.emit(
|
|
63
|
+
'form-builder-FieldsConfirmed',
|
|
64
|
+
plugin?.getCurrentWorkingForm() as RealFormInfo
|
|
65
|
+
);
|
|
66
|
+
plugin.onAfterFieldsSave((processedInfo) => {
|
|
67
|
+
evtBus.emit('form-builder-FieldsConfirmed', processedInfo as RealFormInfo);
|
|
68
|
+
});
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
{confirmedLabel}
|
|
72
|
+
</PrimaryConfirmBtn>
|
|
73
|
+
) : null}
|
|
74
|
+
</FieldCheckerListMsg>
|
|
75
|
+
{fieldsSuffix}
|
|
76
|
+
</MessageContent>
|
|
77
|
+
);
|
|
78
|
+
};
|