@autobe/ui 0.30.4-dev.20260324 → 0.30.4
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/LICENSE +661 -661
- package/lib/components/AutoBeChatMain.js +5 -5
- package/lib/components/AutoBeConfigModal.js +9 -9
- package/package.json +2 -2
- package/src/components/AutoBeAssistantMessageMovie.tsx +22 -22
- package/src/components/AutoBeChatMain.tsx +394 -394
- package/src/components/AutoBeChatSidebar.tsx +414 -414
- package/src/components/AutoBeConfigButton.tsx +83 -83
- package/src/components/AutoBeConfigModal.tsx +443 -443
- package/src/components/AutoBeStatusButton.tsx +75 -75
- package/src/components/AutoBeStatusModal.tsx +486 -486
- package/src/components/AutoBeUserMessageMovie.tsx +27 -27
- package/src/components/common/ActionButton.tsx +205 -205
- package/src/components/common/ActionButtonGroup.tsx +80 -80
- package/src/components/common/AutoBeConfigInput.tsx +185 -185
- package/src/components/common/ChatBubble.tsx +119 -119
- package/src/components/common/Collapsible.tsx +95 -95
- package/src/components/common/CompactSessionIndicator.tsx +73 -73
- package/src/components/common/CompactSessionList.tsx +82 -82
- package/src/components/common/openai/OpenAIContent.tsx +53 -53
- package/src/components/common/openai/OpenAIUserAudioContent.tsx +70 -70
- package/src/components/common/openai/OpenAIUserFileContent.tsx +76 -76
- package/src/components/common/openai/OpenAIUserImageContent.tsx +34 -34
- package/src/components/common/openai/OpenAIUserTextContent.tsx +15 -15
- package/src/components/events/AutoBeCompleteEventMovie.tsx +402 -402
- package/src/components/events/AutoBeCorrectEventMovie.tsx +354 -354
- package/src/components/events/AutoBeEventGroupMovie.tsx +18 -18
- package/src/components/events/AutoBeEventMovie.tsx +154 -154
- package/src/components/events/AutoBeProgressEventMovie.tsx +207 -207
- package/src/components/events/AutoBeScenarioEventMovie.tsx +135 -135
- package/src/components/events/AutoBeStartEventMovie.tsx +82 -82
- package/src/components/events/AutoBeValidateEventMovie.tsx +249 -249
- package/src/components/events/README.md +300 -300
- package/src/components/events/common/CollapsibleEventGroup.tsx +211 -211
- package/src/components/events/common/EventCard.tsx +61 -61
- package/src/components/events/common/EventContent.tsx +31 -31
- package/src/components/events/common/EventHeader.tsx +85 -85
- package/src/components/events/common/EventIcon.tsx +82 -82
- package/src/components/events/common/ProgressBar.tsx +64 -64
- package/src/components/events/groups/CorrectEventGroup.tsx +183 -183
- package/src/components/events/groups/ValidateEventGroup.tsx +143 -143
- package/src/components/events/utils/eventGrouper.tsx +116 -116
- package/src/components/upload/AutoBeChatUploadBox.tsx +433 -433
- package/src/components/upload/AutoBeChatUploadSendButton.tsx +66 -66
- package/src/components/upload/AutoBeFileUploadBox.tsx +123 -123
- package/src/components/upload/AutoBeVoiceRecoderButton.tsx +100 -100
- package/src/context/AutoBeAgentContext.tsx +301 -301
- package/src/context/AutoBeAgentSessionList.tsx +58 -58
- package/src/context/SearchParamsContext.tsx +49 -49
- package/src/icons/Receipt.tsx +74 -74
- package/src/structure/AutoBeListener.ts +368 -368
- package/tsconfig.json +9 -9
- package/README.md +0 -261
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
import { CSSProperties } from "react";
|
|
2
|
-
|
|
3
|
-
export interface IAutoBeConfigInputProps {
|
|
4
|
-
label: string;
|
|
5
|
-
value: string | number;
|
|
6
|
-
onChange: (value: string) => void;
|
|
7
|
-
placeholder?: string;
|
|
8
|
-
type?: "text" | "password" | "url" | "number" | "list";
|
|
9
|
-
icon?: string;
|
|
10
|
-
suggestions?: Array<{ value: string; label?: string }>;
|
|
11
|
-
min?: number;
|
|
12
|
-
max?: number;
|
|
13
|
-
style?: CSSProperties;
|
|
14
|
-
disabled?: boolean;
|
|
15
|
-
required?: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Common input component for configuration forms Supports text, password, url,
|
|
20
|
-
* and number inputs with optional suggestions
|
|
21
|
-
*/
|
|
22
|
-
export const AutoBeConfigInput = (props: IAutoBeConfigInputProps) => {
|
|
23
|
-
const {
|
|
24
|
-
label,
|
|
25
|
-
value,
|
|
26
|
-
onChange,
|
|
27
|
-
placeholder,
|
|
28
|
-
type = "text",
|
|
29
|
-
icon,
|
|
30
|
-
suggestions,
|
|
31
|
-
min,
|
|
32
|
-
max,
|
|
33
|
-
style,
|
|
34
|
-
disabled = false,
|
|
35
|
-
required = false,
|
|
36
|
-
} = props;
|
|
37
|
-
|
|
38
|
-
console.log("suggestions", suggestions);
|
|
39
|
-
const suggestionId = `suggestions-${label.replace(/\s+/g, "-").toLowerCase()}`;
|
|
40
|
-
|
|
41
|
-
// Check if field is required and empty
|
|
42
|
-
const isEmpty =
|
|
43
|
-
value === "" || (typeof value === "string" && value.trim() === "");
|
|
44
|
-
const isRequiredEmpty = required && isEmpty;
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<div style={{ ...style }}>
|
|
48
|
-
<label
|
|
49
|
-
style={{
|
|
50
|
-
display: "block",
|
|
51
|
-
fontSize: "0.875rem",
|
|
52
|
-
fontWeight: "500",
|
|
53
|
-
color: isRequiredEmpty ? "#dc3545" : "#374151",
|
|
54
|
-
marginBottom: "0.5rem",
|
|
55
|
-
}}
|
|
56
|
-
>
|
|
57
|
-
{icon && `${icon} `}
|
|
58
|
-
{label}
|
|
59
|
-
{required && (
|
|
60
|
-
<span style={{ color: "#dc3545", marginLeft: "0.25rem" }}>*</span>
|
|
61
|
-
)}
|
|
62
|
-
</label>
|
|
63
|
-
{type === "list" ? (
|
|
64
|
-
<select
|
|
65
|
-
value={value}
|
|
66
|
-
onChange={(e) => onChange(e.target.value)}
|
|
67
|
-
disabled={disabled}
|
|
68
|
-
style={{
|
|
69
|
-
width: "100%",
|
|
70
|
-
padding: "0.75rem",
|
|
71
|
-
border: `1px solid ${isRequiredEmpty ? "#dc3545" : "#d1d5db"}`,
|
|
72
|
-
borderRadius: "8px",
|
|
73
|
-
fontSize: "0.875rem",
|
|
74
|
-
transition: "border-color 0.2s ease",
|
|
75
|
-
outline: "none",
|
|
76
|
-
boxSizing: "border-box",
|
|
77
|
-
backgroundColor: disabled
|
|
78
|
-
? "#f9fafb"
|
|
79
|
-
: isRequiredEmpty
|
|
80
|
-
? "#fef2f2"
|
|
81
|
-
: "white",
|
|
82
|
-
color: disabled ? "#9ca3af" : "#000000",
|
|
83
|
-
cursor: disabled ? "not-allowed" : "pointer",
|
|
84
|
-
}}
|
|
85
|
-
onFocus={(e) => {
|
|
86
|
-
if (!disabled && !isRequiredEmpty) {
|
|
87
|
-
e.currentTarget.style.borderColor = "#3b82f6";
|
|
88
|
-
}
|
|
89
|
-
}}
|
|
90
|
-
onBlur={(e) => {
|
|
91
|
-
if (!disabled) {
|
|
92
|
-
const newIsEmpty =
|
|
93
|
-
e.currentTarget.value === "" ||
|
|
94
|
-
e.currentTarget.value.trim() === "";
|
|
95
|
-
const newIsRequiredEmpty = required && newIsEmpty;
|
|
96
|
-
e.currentTarget.style.borderColor = newIsRequiredEmpty
|
|
97
|
-
? "#dc3545"
|
|
98
|
-
: "#d1d5db";
|
|
99
|
-
}
|
|
100
|
-
}}
|
|
101
|
-
>
|
|
102
|
-
{placeholder && (
|
|
103
|
-
<option value="" disabled>
|
|
104
|
-
{placeholder}
|
|
105
|
-
</option>
|
|
106
|
-
)}
|
|
107
|
-
{suggestions?.map((suggestion, index) => (
|
|
108
|
-
<option key={index} value={suggestion.value}>
|
|
109
|
-
{suggestion.label || suggestion.value}
|
|
110
|
-
</option>
|
|
111
|
-
))}
|
|
112
|
-
</select>
|
|
113
|
-
) : (
|
|
114
|
-
<input
|
|
115
|
-
type={type}
|
|
116
|
-
value={value}
|
|
117
|
-
onChange={(e) => onChange(e.target.value)}
|
|
118
|
-
placeholder={placeholder}
|
|
119
|
-
list={suggestions ? suggestionId : undefined}
|
|
120
|
-
min={min}
|
|
121
|
-
max={max}
|
|
122
|
-
disabled={disabled}
|
|
123
|
-
style={{
|
|
124
|
-
width: "100%",
|
|
125
|
-
padding: "0.75rem",
|
|
126
|
-
border: `1px solid ${isRequiredEmpty ? "#dc3545" : "#d1d5db"}`,
|
|
127
|
-
borderRadius: "8px",
|
|
128
|
-
fontSize: "0.875rem",
|
|
129
|
-
transition: "border-color 0.2s ease",
|
|
130
|
-
outline: "none",
|
|
131
|
-
boxSizing: "border-box",
|
|
132
|
-
backgroundColor: disabled
|
|
133
|
-
? "#f9fafb"
|
|
134
|
-
: isRequiredEmpty
|
|
135
|
-
? "#fef2f2"
|
|
136
|
-
: "white",
|
|
137
|
-
color: disabled ? "#9ca3af" : "#000000",
|
|
138
|
-
}}
|
|
139
|
-
onFocus={(e) => {
|
|
140
|
-
if (!disabled && !isRequiredEmpty) {
|
|
141
|
-
e.currentTarget.style.borderColor = "#3b82f6";
|
|
142
|
-
}
|
|
143
|
-
}}
|
|
144
|
-
onBlur={(e) => {
|
|
145
|
-
if (!disabled) {
|
|
146
|
-
const newIsEmpty =
|
|
147
|
-
e.currentTarget.value === "" ||
|
|
148
|
-
e.currentTarget.value.trim() === "";
|
|
149
|
-
const newIsRequiredEmpty = required && newIsEmpty;
|
|
150
|
-
e.currentTarget.style.borderColor = newIsRequiredEmpty
|
|
151
|
-
? "#dc3545"
|
|
152
|
-
: "#d1d5db";
|
|
153
|
-
}
|
|
154
|
-
}}
|
|
155
|
-
/>
|
|
156
|
-
)}
|
|
157
|
-
{isRequiredEmpty && (
|
|
158
|
-
<div
|
|
159
|
-
style={{
|
|
160
|
-
fontSize: "0.75rem",
|
|
161
|
-
color: "#dc3545",
|
|
162
|
-
marginTop: "0.25rem",
|
|
163
|
-
display: "flex",
|
|
164
|
-
alignItems: "center",
|
|
165
|
-
gap: "0.25rem",
|
|
166
|
-
}}
|
|
167
|
-
>
|
|
168
|
-
<span>⚠️</span>
|
|
169
|
-
This field is required
|
|
170
|
-
</div>
|
|
171
|
-
)}
|
|
172
|
-
{suggestions && type !== "list" && (
|
|
173
|
-
<datalist id={suggestionId} style={{ cursor: "pointer" }}>
|
|
174
|
-
{suggestions.map((suggestion, index) => (
|
|
175
|
-
<option key={index} value={suggestion.value}>
|
|
176
|
-
{suggestion.label || suggestion.value}
|
|
177
|
-
</option>
|
|
178
|
-
))}
|
|
179
|
-
</datalist>
|
|
180
|
-
)}
|
|
181
|
-
</div>
|
|
182
|
-
);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
export default AutoBeConfigInput;
|
|
1
|
+
import { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
export interface IAutoBeConfigInputProps {
|
|
4
|
+
label: string;
|
|
5
|
+
value: string | number;
|
|
6
|
+
onChange: (value: string) => void;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
type?: "text" | "password" | "url" | "number" | "list";
|
|
9
|
+
icon?: string;
|
|
10
|
+
suggestions?: Array<{ value: string; label?: string }>;
|
|
11
|
+
min?: number;
|
|
12
|
+
max?: number;
|
|
13
|
+
style?: CSSProperties;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
required?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Common input component for configuration forms Supports text, password, url,
|
|
20
|
+
* and number inputs with optional suggestions
|
|
21
|
+
*/
|
|
22
|
+
export const AutoBeConfigInput = (props: IAutoBeConfigInputProps) => {
|
|
23
|
+
const {
|
|
24
|
+
label,
|
|
25
|
+
value,
|
|
26
|
+
onChange,
|
|
27
|
+
placeholder,
|
|
28
|
+
type = "text",
|
|
29
|
+
icon,
|
|
30
|
+
suggestions,
|
|
31
|
+
min,
|
|
32
|
+
max,
|
|
33
|
+
style,
|
|
34
|
+
disabled = false,
|
|
35
|
+
required = false,
|
|
36
|
+
} = props;
|
|
37
|
+
|
|
38
|
+
console.log("suggestions", suggestions);
|
|
39
|
+
const suggestionId = `suggestions-${label.replace(/\s+/g, "-").toLowerCase()}`;
|
|
40
|
+
|
|
41
|
+
// Check if field is required and empty
|
|
42
|
+
const isEmpty =
|
|
43
|
+
value === "" || (typeof value === "string" && value.trim() === "");
|
|
44
|
+
const isRequiredEmpty = required && isEmpty;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div style={{ ...style }}>
|
|
48
|
+
<label
|
|
49
|
+
style={{
|
|
50
|
+
display: "block",
|
|
51
|
+
fontSize: "0.875rem",
|
|
52
|
+
fontWeight: "500",
|
|
53
|
+
color: isRequiredEmpty ? "#dc3545" : "#374151",
|
|
54
|
+
marginBottom: "0.5rem",
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
{icon && `${icon} `}
|
|
58
|
+
{label}
|
|
59
|
+
{required && (
|
|
60
|
+
<span style={{ color: "#dc3545", marginLeft: "0.25rem" }}>*</span>
|
|
61
|
+
)}
|
|
62
|
+
</label>
|
|
63
|
+
{type === "list" ? (
|
|
64
|
+
<select
|
|
65
|
+
value={value}
|
|
66
|
+
onChange={(e) => onChange(e.target.value)}
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
style={{
|
|
69
|
+
width: "100%",
|
|
70
|
+
padding: "0.75rem",
|
|
71
|
+
border: `1px solid ${isRequiredEmpty ? "#dc3545" : "#d1d5db"}`,
|
|
72
|
+
borderRadius: "8px",
|
|
73
|
+
fontSize: "0.875rem",
|
|
74
|
+
transition: "border-color 0.2s ease",
|
|
75
|
+
outline: "none",
|
|
76
|
+
boxSizing: "border-box",
|
|
77
|
+
backgroundColor: disabled
|
|
78
|
+
? "#f9fafb"
|
|
79
|
+
: isRequiredEmpty
|
|
80
|
+
? "#fef2f2"
|
|
81
|
+
: "white",
|
|
82
|
+
color: disabled ? "#9ca3af" : "#000000",
|
|
83
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
84
|
+
}}
|
|
85
|
+
onFocus={(e) => {
|
|
86
|
+
if (!disabled && !isRequiredEmpty) {
|
|
87
|
+
e.currentTarget.style.borderColor = "#3b82f6";
|
|
88
|
+
}
|
|
89
|
+
}}
|
|
90
|
+
onBlur={(e) => {
|
|
91
|
+
if (!disabled) {
|
|
92
|
+
const newIsEmpty =
|
|
93
|
+
e.currentTarget.value === "" ||
|
|
94
|
+
e.currentTarget.value.trim() === "";
|
|
95
|
+
const newIsRequiredEmpty = required && newIsEmpty;
|
|
96
|
+
e.currentTarget.style.borderColor = newIsRequiredEmpty
|
|
97
|
+
? "#dc3545"
|
|
98
|
+
: "#d1d5db";
|
|
99
|
+
}
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
{placeholder && (
|
|
103
|
+
<option value="" disabled>
|
|
104
|
+
{placeholder}
|
|
105
|
+
</option>
|
|
106
|
+
)}
|
|
107
|
+
{suggestions?.map((suggestion, index) => (
|
|
108
|
+
<option key={index} value={suggestion.value}>
|
|
109
|
+
{suggestion.label || suggestion.value}
|
|
110
|
+
</option>
|
|
111
|
+
))}
|
|
112
|
+
</select>
|
|
113
|
+
) : (
|
|
114
|
+
<input
|
|
115
|
+
type={type}
|
|
116
|
+
value={value}
|
|
117
|
+
onChange={(e) => onChange(e.target.value)}
|
|
118
|
+
placeholder={placeholder}
|
|
119
|
+
list={suggestions ? suggestionId : undefined}
|
|
120
|
+
min={min}
|
|
121
|
+
max={max}
|
|
122
|
+
disabled={disabled}
|
|
123
|
+
style={{
|
|
124
|
+
width: "100%",
|
|
125
|
+
padding: "0.75rem",
|
|
126
|
+
border: `1px solid ${isRequiredEmpty ? "#dc3545" : "#d1d5db"}`,
|
|
127
|
+
borderRadius: "8px",
|
|
128
|
+
fontSize: "0.875rem",
|
|
129
|
+
transition: "border-color 0.2s ease",
|
|
130
|
+
outline: "none",
|
|
131
|
+
boxSizing: "border-box",
|
|
132
|
+
backgroundColor: disabled
|
|
133
|
+
? "#f9fafb"
|
|
134
|
+
: isRequiredEmpty
|
|
135
|
+
? "#fef2f2"
|
|
136
|
+
: "white",
|
|
137
|
+
color: disabled ? "#9ca3af" : "#000000",
|
|
138
|
+
}}
|
|
139
|
+
onFocus={(e) => {
|
|
140
|
+
if (!disabled && !isRequiredEmpty) {
|
|
141
|
+
e.currentTarget.style.borderColor = "#3b82f6";
|
|
142
|
+
}
|
|
143
|
+
}}
|
|
144
|
+
onBlur={(e) => {
|
|
145
|
+
if (!disabled) {
|
|
146
|
+
const newIsEmpty =
|
|
147
|
+
e.currentTarget.value === "" ||
|
|
148
|
+
e.currentTarget.value.trim() === "";
|
|
149
|
+
const newIsRequiredEmpty = required && newIsEmpty;
|
|
150
|
+
e.currentTarget.style.borderColor = newIsRequiredEmpty
|
|
151
|
+
? "#dc3545"
|
|
152
|
+
: "#d1d5db";
|
|
153
|
+
}
|
|
154
|
+
}}
|
|
155
|
+
/>
|
|
156
|
+
)}
|
|
157
|
+
{isRequiredEmpty && (
|
|
158
|
+
<div
|
|
159
|
+
style={{
|
|
160
|
+
fontSize: "0.75rem",
|
|
161
|
+
color: "#dc3545",
|
|
162
|
+
marginTop: "0.25rem",
|
|
163
|
+
display: "flex",
|
|
164
|
+
alignItems: "center",
|
|
165
|
+
gap: "0.25rem",
|
|
166
|
+
}}
|
|
167
|
+
>
|
|
168
|
+
<span>⚠️</span>
|
|
169
|
+
This field is required
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
{suggestions && type !== "list" && (
|
|
173
|
+
<datalist id={suggestionId} style={{ cursor: "pointer" }}>
|
|
174
|
+
{suggestions.map((suggestion, index) => (
|
|
175
|
+
<option key={index} value={suggestion.value}>
|
|
176
|
+
{suggestion.label || suggestion.value}
|
|
177
|
+
</option>
|
|
178
|
+
))}
|
|
179
|
+
</datalist>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
export default AutoBeConfigInput;
|
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
import { AutoBeUserConversateContent } from "@autobe/interface";
|
|
2
|
-
|
|
3
|
-
import { formatTime } from "../../utils/time";
|
|
4
|
-
import { OpenAIContent } from "./openai";
|
|
5
|
-
|
|
6
|
-
/** Props interface for ChatBubble component */
|
|
7
|
-
export interface IChatBubbleProps {
|
|
8
|
-
/** Message content - supports text, audio, file, and image types */
|
|
9
|
-
content: Array<AutoBeUserConversateContent | string>;
|
|
10
|
-
|
|
11
|
-
/** Direction of the chat bubble - left or right */
|
|
12
|
-
direction: "left" | "right";
|
|
13
|
-
/** Timestamp (ISO format) */
|
|
14
|
-
timestamp?: string;
|
|
15
|
-
/** Assistant name (default: "Assistant") */
|
|
16
|
-
assistantName?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Props interface for content renderer functions */
|
|
20
|
-
export interface IContentRendererProps {
|
|
21
|
-
/** Whether the bubble is positioned on the right side */
|
|
22
|
-
isRight: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const ChatBubble = (props: IChatBubbleProps) => {
|
|
26
|
-
const { content, direction, timestamp, assistantName = "Assistant" } = props;
|
|
27
|
-
|
|
28
|
-
const isRight = direction === "right";
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<div
|
|
32
|
-
style={{
|
|
33
|
-
display: "flex",
|
|
34
|
-
marginBottom: "1rem",
|
|
35
|
-
justifyContent: isRight ? "flex-end" : "flex-start",
|
|
36
|
-
}}
|
|
37
|
-
>
|
|
38
|
-
<div
|
|
39
|
-
style={{
|
|
40
|
-
display: "flex",
|
|
41
|
-
flexDirection: "column",
|
|
42
|
-
maxWidth: "48rem",
|
|
43
|
-
alignItems: isRight ? "flex-end" : "flex-start",
|
|
44
|
-
}}
|
|
45
|
-
>
|
|
46
|
-
{/* User name/time */}
|
|
47
|
-
<div
|
|
48
|
-
style={{
|
|
49
|
-
marginBottom: "0.25rem",
|
|
50
|
-
textAlign: isRight ? "right" : "left",
|
|
51
|
-
}}
|
|
52
|
-
>
|
|
53
|
-
<span
|
|
54
|
-
style={{
|
|
55
|
-
fontSize: "0.75rem",
|
|
56
|
-
lineHeight: "1rem",
|
|
57
|
-
color: "#6b7280",
|
|
58
|
-
}}
|
|
59
|
-
>
|
|
60
|
-
{isRight ? "You" : assistantName}
|
|
61
|
-
{timestamp && (
|
|
62
|
-
<>
|
|
63
|
-
<span
|
|
64
|
-
style={{
|
|
65
|
-
marginLeft: "0.25rem",
|
|
66
|
-
marginRight: "0.25rem",
|
|
67
|
-
}}
|
|
68
|
-
>
|
|
69
|
-
•
|
|
70
|
-
</span>
|
|
71
|
-
{formatTime(timestamp)}
|
|
72
|
-
</>
|
|
73
|
-
)}
|
|
74
|
-
</span>
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
{/* Message bubble */}
|
|
78
|
-
<div
|
|
79
|
-
style={{
|
|
80
|
-
position: "relative",
|
|
81
|
-
maxWidth: "32rem",
|
|
82
|
-
paddingLeft: "1rem",
|
|
83
|
-
paddingRight: "1rem",
|
|
84
|
-
paddingTop: "0.75rem",
|
|
85
|
-
paddingBottom: "0.75rem",
|
|
86
|
-
borderRadius: "1rem",
|
|
87
|
-
boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
|
88
|
-
backgroundColor: isRight ? "#3b82f6" : "#f3f4f6",
|
|
89
|
-
color: isRight ? "#ffffff" : "#1f2937",
|
|
90
|
-
borderTopRightRadius: isRight ? "0.375rem" : "1rem",
|
|
91
|
-
borderTopLeftRadius: isRight ? "1rem" : "0.375rem",
|
|
92
|
-
border: isRight ? "none" : "1px solid #e5e7eb",
|
|
93
|
-
}}
|
|
94
|
-
>
|
|
95
|
-
{/* Bubble tail */}
|
|
96
|
-
<div
|
|
97
|
-
style={{
|
|
98
|
-
position: "absolute",
|
|
99
|
-
width: "0.75rem",
|
|
100
|
-
height: "0.75rem",
|
|
101
|
-
transform: "rotate(45deg)",
|
|
102
|
-
backgroundColor: isRight ? "#3b82f6" : "#f3f4f6",
|
|
103
|
-
right: isRight ? "-0.25rem" : undefined,
|
|
104
|
-
left: isRight ? undefined : "-0.25rem",
|
|
105
|
-
top: "0.75rem",
|
|
106
|
-
borderLeft: isRight ? "none" : "1px solid #e5e7eb",
|
|
107
|
-
borderBottom: isRight ? "none" : "1px solid #e5e7eb",
|
|
108
|
-
}}
|
|
109
|
-
/>
|
|
110
|
-
|
|
111
|
-
{/* Message content */}
|
|
112
|
-
<OpenAIContent content={content} />
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
);
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
export default ChatBubble;
|
|
1
|
+
import { AutoBeUserConversateContent } from "@autobe/interface";
|
|
2
|
+
|
|
3
|
+
import { formatTime } from "../../utils/time";
|
|
4
|
+
import { OpenAIContent } from "./openai";
|
|
5
|
+
|
|
6
|
+
/** Props interface for ChatBubble component */
|
|
7
|
+
export interface IChatBubbleProps {
|
|
8
|
+
/** Message content - supports text, audio, file, and image types */
|
|
9
|
+
content: Array<AutoBeUserConversateContent | string>;
|
|
10
|
+
|
|
11
|
+
/** Direction of the chat bubble - left or right */
|
|
12
|
+
direction: "left" | "right";
|
|
13
|
+
/** Timestamp (ISO format) */
|
|
14
|
+
timestamp?: string;
|
|
15
|
+
/** Assistant name (default: "Assistant") */
|
|
16
|
+
assistantName?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Props interface for content renderer functions */
|
|
20
|
+
export interface IContentRendererProps {
|
|
21
|
+
/** Whether the bubble is positioned on the right side */
|
|
22
|
+
isRight: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ChatBubble = (props: IChatBubbleProps) => {
|
|
26
|
+
const { content, direction, timestamp, assistantName = "Assistant" } = props;
|
|
27
|
+
|
|
28
|
+
const isRight = direction === "right";
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
style={{
|
|
33
|
+
display: "flex",
|
|
34
|
+
marginBottom: "1rem",
|
|
35
|
+
justifyContent: isRight ? "flex-end" : "flex-start",
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<div
|
|
39
|
+
style={{
|
|
40
|
+
display: "flex",
|
|
41
|
+
flexDirection: "column",
|
|
42
|
+
maxWidth: "48rem",
|
|
43
|
+
alignItems: isRight ? "flex-end" : "flex-start",
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
{/* User name/time */}
|
|
47
|
+
<div
|
|
48
|
+
style={{
|
|
49
|
+
marginBottom: "0.25rem",
|
|
50
|
+
textAlign: isRight ? "right" : "left",
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<span
|
|
54
|
+
style={{
|
|
55
|
+
fontSize: "0.75rem",
|
|
56
|
+
lineHeight: "1rem",
|
|
57
|
+
color: "#6b7280",
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{isRight ? "You" : assistantName}
|
|
61
|
+
{timestamp && (
|
|
62
|
+
<>
|
|
63
|
+
<span
|
|
64
|
+
style={{
|
|
65
|
+
marginLeft: "0.25rem",
|
|
66
|
+
marginRight: "0.25rem",
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
•
|
|
70
|
+
</span>
|
|
71
|
+
{formatTime(timestamp)}
|
|
72
|
+
</>
|
|
73
|
+
)}
|
|
74
|
+
</span>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
{/* Message bubble */}
|
|
78
|
+
<div
|
|
79
|
+
style={{
|
|
80
|
+
position: "relative",
|
|
81
|
+
maxWidth: "32rem",
|
|
82
|
+
paddingLeft: "1rem",
|
|
83
|
+
paddingRight: "1rem",
|
|
84
|
+
paddingTop: "0.75rem",
|
|
85
|
+
paddingBottom: "0.75rem",
|
|
86
|
+
borderRadius: "1rem",
|
|
87
|
+
boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
|
88
|
+
backgroundColor: isRight ? "#3b82f6" : "#f3f4f6",
|
|
89
|
+
color: isRight ? "#ffffff" : "#1f2937",
|
|
90
|
+
borderTopRightRadius: isRight ? "0.375rem" : "1rem",
|
|
91
|
+
borderTopLeftRadius: isRight ? "1rem" : "0.375rem",
|
|
92
|
+
border: isRight ? "none" : "1px solid #e5e7eb",
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
{/* Bubble tail */}
|
|
96
|
+
<div
|
|
97
|
+
style={{
|
|
98
|
+
position: "absolute",
|
|
99
|
+
width: "0.75rem",
|
|
100
|
+
height: "0.75rem",
|
|
101
|
+
transform: "rotate(45deg)",
|
|
102
|
+
backgroundColor: isRight ? "#3b82f6" : "#f3f4f6",
|
|
103
|
+
right: isRight ? "-0.25rem" : undefined,
|
|
104
|
+
left: isRight ? undefined : "-0.25rem",
|
|
105
|
+
top: "0.75rem",
|
|
106
|
+
borderLeft: isRight ? "none" : "1px solid #e5e7eb",
|
|
107
|
+
borderBottom: isRight ? "none" : "1px solid #e5e7eb",
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
|
|
111
|
+
{/* Message content */}
|
|
112
|
+
<OpenAIContent content={content} />
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default ChatBubble;
|