@autobe/ui 0.29.2 → 0.30.0-dev.20260315

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 (151) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +261 -0
  3. package/lib/components/AutoBeChatMain.js +5 -5
  4. package/lib/components/AutoBeChatMain.js.map +1 -1
  5. package/lib/components/AutoBeConfigModal.js +9 -9
  6. package/lib/components/AutoBeStatusModal.js +4 -4
  7. package/lib/components/AutoBeStatusModal.js.map +1 -1
  8. package/lib/components/AutoBeUserMessageMovie.d.ts +2 -2
  9. package/lib/components/common/ChatBubble.d.ts +2 -2
  10. package/lib/components/common/openai/OpenAIContent.d.ts +2 -2
  11. package/lib/components/common/openai/OpenAIContent.js.map +1 -1
  12. package/lib/components/common/openai/OpenAIUserAudioContent.js +1 -1
  13. package/lib/components/common/openai/OpenAIUserAudioContent.js.map +1 -1
  14. package/lib/components/common/openai/OpenAIUserFileContent.js +1 -1
  15. package/lib/components/common/openai/OpenAIUserFileContent.js.map +1 -1
  16. package/lib/components/common/openai/OpenAIUserImageContent.d.ts +2 -2
  17. package/lib/components/events/AutoBeCompleteEventMovie.d.ts +2 -2
  18. package/lib/components/events/AutoBeCompleteEventMovie.js +5 -5
  19. package/lib/components/events/AutoBeCompleteEventMovie.js.map +1 -1
  20. package/lib/components/events/AutoBeCorrectEventMovie.d.ts +2 -2
  21. package/lib/components/events/AutoBeCorrectEventMovie.js +4 -4
  22. package/lib/components/events/AutoBeCorrectEventMovie.js.map +1 -1
  23. package/lib/components/events/AutoBeEventMovie.js +38 -17
  24. package/lib/components/events/AutoBeEventMovie.js.map +1 -1
  25. package/lib/components/events/AutoBeProgressEventMovie.js +73 -13
  26. package/lib/components/events/AutoBeProgressEventMovie.js.map +1 -1
  27. package/lib/components/events/AutoBeScenarioEventMovie.d.ts +2 -2
  28. package/lib/components/events/AutoBeScenarioEventMovie.js +18 -5
  29. package/lib/components/events/AutoBeScenarioEventMovie.js.map +1 -1
  30. package/lib/components/events/AutoBeStartEventMovie.d.ts +2 -2
  31. package/lib/components/events/AutoBeStartEventMovie.js +2 -2
  32. package/lib/components/events/AutoBeStartEventMovie.js.map +1 -1
  33. package/lib/components/events/AutoBeValidateEventMovie.d.ts +2 -2
  34. package/lib/components/events/AutoBeValidateEventMovie.js +3 -11
  35. package/lib/components/events/AutoBeValidateEventMovie.js.map +1 -1
  36. package/lib/components/events/groups/CorrectEventGroup.d.ts +2 -2
  37. package/lib/components/events/groups/CorrectEventGroup.js +1 -1
  38. package/lib/components/events/groups/CorrectEventGroup.js.map +1 -1
  39. package/lib/components/events/groups/ValidateEventGroup.d.ts +2 -2
  40. package/lib/components/events/groups/ValidateEventGroup.js +1 -2
  41. package/lib/components/events/groups/ValidateEventGroup.js.map +1 -1
  42. package/lib/components/events/utils/eventGrouper.js +1 -2
  43. package/lib/components/events/utils/eventGrouper.js.map +1 -1
  44. package/lib/components/upload/AutoBeChatUploadBox.d.ts +3 -4
  45. package/lib/components/upload/AutoBeChatUploadBox.js +2 -1
  46. package/lib/components/upload/AutoBeChatUploadBox.js.map +1 -1
  47. package/lib/components/upload/AutoBeChatUploadSendButton.js +1 -1
  48. package/lib/components/upload/AutoBeChatUploadSendButton.js.map +1 -1
  49. package/lib/context/AutoBeAgentContext.d.ts +1 -3
  50. package/lib/context/AutoBeAgentContext.js +0 -4
  51. package/lib/context/AutoBeAgentContext.js.map +1 -1
  52. package/lib/hooks/useSessionStorage.d.ts +4 -0
  53. package/lib/hooks/useSessionStorage.js +16 -0
  54. package/lib/hooks/useSessionStorage.js.map +1 -0
  55. package/lib/index.d.ts +1 -0
  56. package/lib/index.js +1 -0
  57. package/lib/index.js.map +1 -1
  58. package/lib/strategy/AutoBeAgentSessionStorageStrategy.d.ts +10 -0
  59. package/lib/strategy/AutoBeAgentSessionStorageStrategy.js +117 -0
  60. package/lib/strategy/AutoBeAgentSessionStorageStrategy.js.map +1 -0
  61. package/lib/structure/AutoBeListener.js +91 -23
  62. package/lib/structure/AutoBeListener.js.map +1 -1
  63. package/lib/structure/AutoBeListenerState.d.ts +3 -3
  64. package/lib/structure/AutoBeListenerState.js +4 -4
  65. package/lib/structure/AutoBeListenerState.js.map +1 -1
  66. package/lib/structure/IAutoBeAgentSessionStorageStrategy.js +1 -1
  67. package/lib/structure/IAutoBeAgentSessionStorageStrategy.js.map +1 -1
  68. package/lib/utils/AutoBeFileUploader.d.ts +2 -2
  69. package/lib/utils/AutoBeFileUploader.js.map +1 -1
  70. package/package.json +3 -4
  71. package/src/components/AutoBeAssistantMessageMovie.tsx +22 -22
  72. package/src/components/AutoBeChatMain.tsx +376 -376
  73. package/src/components/AutoBeChatSidebar.tsx +414 -414
  74. package/src/components/AutoBeConfigButton.tsx +83 -83
  75. package/src/components/AutoBeConfigModal.tsx +443 -443
  76. package/src/components/AutoBeStatusButton.tsx +75 -75
  77. package/src/components/AutoBeStatusModal.tsx +486 -484
  78. package/src/components/AutoBeUserMessageMovie.tsx +27 -27
  79. package/src/components/common/ActionButton.tsx +205 -205
  80. package/src/components/common/ActionButtonGroup.tsx +80 -80
  81. package/src/components/common/AutoBeConfigInput.tsx +185 -185
  82. package/src/components/common/ChatBubble.tsx +119 -119
  83. package/src/components/common/Collapsible.tsx +95 -95
  84. package/src/components/common/CompactSessionIndicator.tsx +73 -73
  85. package/src/components/common/CompactSessionList.tsx +82 -82
  86. package/src/components/common/index.ts +8 -8
  87. package/src/components/common/openai/OpenAIContent.tsx +53 -53
  88. package/src/components/common/openai/OpenAIUserAudioContent.tsx +70 -70
  89. package/src/components/common/openai/OpenAIUserFileContent.tsx +76 -76
  90. package/src/components/common/openai/OpenAIUserImageContent.tsx +34 -34
  91. package/src/components/common/openai/OpenAIUserTextContent.tsx +15 -15
  92. package/src/components/common/openai/index.ts +5 -5
  93. package/src/components/events/AutoBeCompleteEventMovie.tsx +402 -402
  94. package/src/components/events/AutoBeCorrectEventMovie.tsx +354 -368
  95. package/src/components/events/AutoBeEventGroupMovie.tsx +18 -18
  96. package/src/components/events/AutoBeEventMovie.tsx +158 -139
  97. package/src/components/events/AutoBeProgressEventMovie.tsx +217 -157
  98. package/src/components/events/AutoBeScenarioEventMovie.tsx +135 -95
  99. package/src/components/events/AutoBeStartEventMovie.tsx +82 -82
  100. package/src/components/events/AutoBeValidateEventMovie.tsx +249 -286
  101. package/src/components/events/README.md +300 -300
  102. package/src/components/events/common/CollapsibleEventGroup.tsx +211 -211
  103. package/src/components/events/common/EventCard.tsx +61 -61
  104. package/src/components/events/common/EventContent.tsx +31 -31
  105. package/src/components/events/common/EventHeader.tsx +85 -85
  106. package/src/components/events/common/EventIcon.tsx +82 -82
  107. package/src/components/events/common/ProgressBar.tsx +64 -64
  108. package/src/components/events/common/index.ts +13 -13
  109. package/src/components/events/groups/CorrectEventGroup.tsx +183 -183
  110. package/src/components/events/groups/ValidateEventGroup.tsx +143 -146
  111. package/src/components/events/groups/index.ts +8 -8
  112. package/src/components/events/index.ts +16 -16
  113. package/src/components/events/utils/eventGrouper.tsx +116 -117
  114. package/src/components/events/utils/index.ts +1 -1
  115. package/src/components/index.ts +13 -13
  116. package/src/components/upload/AutoBeChatUploadBox.tsx +425 -424
  117. package/src/components/upload/AutoBeChatUploadSendButton.tsx +66 -66
  118. package/src/components/upload/AutoBeFileUploadBox.tsx +123 -123
  119. package/src/components/upload/AutoBeUploadConfig.ts +5 -5
  120. package/src/components/upload/AutoBeVoiceRecoderButton.tsx +100 -100
  121. package/src/components/upload/index.ts +5 -5
  122. package/src/constant/color.ts +28 -28
  123. package/src/context/AutoBeAgentContext.tsx +245 -258
  124. package/src/context/AutoBeAgentSessionList.tsx +58 -58
  125. package/src/context/SearchParamsContext.tsx +49 -49
  126. package/src/hooks/index.ts +3 -3
  127. package/src/hooks/useEscapeKey.ts +24 -24
  128. package/src/hooks/useIsomorphicLayoutEffect.ts +8 -8
  129. package/src/hooks/useMediaQuery.ts +73 -73
  130. package/src/hooks/useSessionStorage.ts +10 -0
  131. package/src/icons/Receipt.tsx +74 -74
  132. package/src/index.ts +9 -8
  133. package/src/strategy/AutoBeAgentSessionStorageStrategy.ts +127 -0
  134. package/src/structure/AutoBeListener.ts +373 -304
  135. package/src/structure/AutoBeListenerState.ts +53 -53
  136. package/src/structure/IAutoBeAgentSessionStorageStrategy.ts +87 -87
  137. package/src/structure/IAutoBeEventGroup.ts +6 -6
  138. package/src/structure/index.ts +4 -4
  139. package/src/types/config.ts +44 -44
  140. package/src/types/index.ts +1 -1
  141. package/src/utils/AutoBeFileUploader.ts +279 -279
  142. package/src/utils/AutoBeVoiceRecorder.ts +95 -95
  143. package/src/utils/__tests__/crypto.test.ts +286 -286
  144. package/src/utils/__tests__/storage.test.ts +229 -229
  145. package/src/utils/crypto.ts +95 -95
  146. package/src/utils/index.ts +6 -6
  147. package/src/utils/number.ts +17 -17
  148. package/src/utils/storage.ts +96 -96
  149. package/src/utils/time.ts +14 -14
  150. package/tsconfig.json +9 -9
  151. package/vitest.config.ts +15 -15
@@ -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 { AutoBeUserMessageContent } 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<AutoBeUserMessageContent | 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;