@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.
Files changed (53) hide show
  1. package/LICENSE +661 -661
  2. package/lib/components/AutoBeChatMain.js +5 -5
  3. package/lib/components/AutoBeConfigModal.js +9 -9
  4. package/package.json +2 -2
  5. package/src/components/AutoBeAssistantMessageMovie.tsx +22 -22
  6. package/src/components/AutoBeChatMain.tsx +394 -394
  7. package/src/components/AutoBeChatSidebar.tsx +414 -414
  8. package/src/components/AutoBeConfigButton.tsx +83 -83
  9. package/src/components/AutoBeConfigModal.tsx +443 -443
  10. package/src/components/AutoBeStatusButton.tsx +75 -75
  11. package/src/components/AutoBeStatusModal.tsx +486 -486
  12. package/src/components/AutoBeUserMessageMovie.tsx +27 -27
  13. package/src/components/common/ActionButton.tsx +205 -205
  14. package/src/components/common/ActionButtonGroup.tsx +80 -80
  15. package/src/components/common/AutoBeConfigInput.tsx +185 -185
  16. package/src/components/common/ChatBubble.tsx +119 -119
  17. package/src/components/common/Collapsible.tsx +95 -95
  18. package/src/components/common/CompactSessionIndicator.tsx +73 -73
  19. package/src/components/common/CompactSessionList.tsx +82 -82
  20. package/src/components/common/openai/OpenAIContent.tsx +53 -53
  21. package/src/components/common/openai/OpenAIUserAudioContent.tsx +70 -70
  22. package/src/components/common/openai/OpenAIUserFileContent.tsx +76 -76
  23. package/src/components/common/openai/OpenAIUserImageContent.tsx +34 -34
  24. package/src/components/common/openai/OpenAIUserTextContent.tsx +15 -15
  25. package/src/components/events/AutoBeCompleteEventMovie.tsx +402 -402
  26. package/src/components/events/AutoBeCorrectEventMovie.tsx +354 -354
  27. package/src/components/events/AutoBeEventGroupMovie.tsx +18 -18
  28. package/src/components/events/AutoBeEventMovie.tsx +154 -154
  29. package/src/components/events/AutoBeProgressEventMovie.tsx +207 -207
  30. package/src/components/events/AutoBeScenarioEventMovie.tsx +135 -135
  31. package/src/components/events/AutoBeStartEventMovie.tsx +82 -82
  32. package/src/components/events/AutoBeValidateEventMovie.tsx +249 -249
  33. package/src/components/events/README.md +300 -300
  34. package/src/components/events/common/CollapsibleEventGroup.tsx +211 -211
  35. package/src/components/events/common/EventCard.tsx +61 -61
  36. package/src/components/events/common/EventContent.tsx +31 -31
  37. package/src/components/events/common/EventHeader.tsx +85 -85
  38. package/src/components/events/common/EventIcon.tsx +82 -82
  39. package/src/components/events/common/ProgressBar.tsx +64 -64
  40. package/src/components/events/groups/CorrectEventGroup.tsx +183 -183
  41. package/src/components/events/groups/ValidateEventGroup.tsx +143 -143
  42. package/src/components/events/utils/eventGrouper.tsx +116 -116
  43. package/src/components/upload/AutoBeChatUploadBox.tsx +433 -433
  44. package/src/components/upload/AutoBeChatUploadSendButton.tsx +66 -66
  45. package/src/components/upload/AutoBeFileUploadBox.tsx +123 -123
  46. package/src/components/upload/AutoBeVoiceRecoderButton.tsx +100 -100
  47. package/src/context/AutoBeAgentContext.tsx +301 -301
  48. package/src/context/AutoBeAgentSessionList.tsx +58 -58
  49. package/src/context/SearchParamsContext.tsx +49 -49
  50. package/src/icons/Receipt.tsx +74 -74
  51. package/src/structure/AutoBeListener.ts +368 -368
  52. package/tsconfig.json +9 -9
  53. package/README.md +0 -261
@@ -1,301 +1,301 @@
1
- import { IAutoBeRpcService, IAutoBeTokenUsageJson } from "@autobe/interface";
2
- import {
3
- ReactNode,
4
- createContext,
5
- useCallback,
6
- useContext,
7
- useEffect,
8
- useRef,
9
- useState,
10
- } from "react";
11
-
12
- import {
13
- AutoBeListener,
14
- AutoBeListenerState,
15
- IAutoBeAgentSessionStorageStrategy,
16
- IAutoBeEventGroup,
17
- } from "../structure";
18
- import { IAutoBeConfig } from "../types/config";
19
- import { useAutoBeAgentSessionList } from "./AutoBeAgentSessionList";
20
- import { useSearchParams } from "./SearchParamsContext";
21
-
22
- export interface IAutoBeServiceData {
23
- service: IAutoBeRpcService;
24
- listener: AutoBeListener;
25
- close: () => void | Promise<void>;
26
- sessionId: string;
27
- }
28
-
29
- export type AutoBeServiceFactory = (
30
- config: IAutoBeConfig,
31
- ) => Promise<IAutoBeServiceData>;
32
-
33
- export type AutoBeConnectionStatus =
34
- | "disconnected"
35
- | "connecting"
36
- | "connected";
37
-
38
- interface AutoBeAgentContextType {
39
- // Service state
40
- connectionStatus: AutoBeConnectionStatus;
41
-
42
- // Service data (available when ready)
43
- eventGroups: IAutoBeEventGroup[];
44
- tokenUsage: IAutoBeTokenUsageJson | null;
45
- state: AutoBeListenerState | null;
46
- service: IAutoBeRpcService | null;
47
- listener: AutoBeListener | null;
48
-
49
- // Service management
50
- getAutoBeService: (config?: IAutoBeConfig) => Promise<IAutoBeServiceData>;
51
- resetService: () => void;
52
- }
53
-
54
- const AutoBeAgentContext = createContext<AutoBeAgentContextType | null>(null);
55
-
56
- export function AutoBeAgentProvider({
57
- children,
58
- serviceFactory,
59
- storageStrategy,
60
- }: {
61
- serviceFactory: AutoBeServiceFactory;
62
- children: ReactNode;
63
- storageStrategy: IAutoBeAgentSessionStorageStrategy;
64
- }) {
65
- // Service state
66
- const [connectionStatus, setConnectionStatus] =
67
- useState<AutoBeConnectionStatus>("disconnected");
68
-
69
- // Service data
70
- const { searchParams, setSearchParams } = useSearchParams();
71
- // Use URL parameter for conversation ID - enables bookmark/share support
72
- const activeConversationId = searchParams.get("session-id") ?? null;
73
-
74
- const [tokenUsage, setTokenUsage] = useState<IAutoBeTokenUsageJson | null>(
75
- null,
76
- );
77
- const [eventGroups, setEventGroups] = useState<IAutoBeEventGroup[]>([]);
78
-
79
- // Store service instance in a ref to avoid React dev mode inspecting
80
- // the TGrid driver Proxy (which crashes on Symbol property access).
81
- // connectionStatus state changes drive re-renders instead.
82
- const serviceInstanceRef = useRef<IAutoBeServiceData | null>(null);
83
-
84
- const { refreshSessionList } = useAutoBeAgentSessionList();
85
- // Context-scoped service getter
86
- const getAutoBeService = useCallback(
87
- async (
88
- config: IAutoBeConfig = {} as IAutoBeConfig,
89
- ): Promise<IAutoBeServiceData> => {
90
- // Return existing instance if available
91
- if (serviceInstanceRef.current && connectionStatus === "connected") {
92
- return serviceInstanceRef.current;
93
- }
94
-
95
- // Prevent multiple concurrent creations
96
- if (connectionStatus === "connecting") {
97
- throw new Error("Service is already connecting. Please wait.");
98
- }
99
-
100
- if (!serviceFactory) {
101
- throw new Error("No service factory provided. Cannot create service.");
102
- }
103
-
104
- try {
105
- setConnectionStatus("connecting");
106
-
107
- // Create new service instance
108
- const newServiceData = await serviceFactory({
109
- ...config,
110
- sessionId: activeConversationId,
111
- });
112
- // Wrap the TGrid driver proxy to handle Symbol access from
113
- // React dev-mode's render logging without crashing.
114
- newServiceData.service = wrapServiceProxy(newServiceData.service);
115
- serviceInstanceRef.current = newServiceData;
116
-
117
- setSearchParams((sp) => {
118
- const newSp = new URLSearchParams(sp);
119
- newSp.set("session-id", newServiceData.sessionId);
120
- return newSp;
121
- });
122
- setConnectionStatus("connected");
123
-
124
- return newServiceData;
125
- } catch (error) {
126
- setConnectionStatus("disconnected");
127
- throw error;
128
- }
129
- },
130
- [
131
- serviceFactory,
132
- connectionStatus,
133
- activeConversationId,
134
- searchParams,
135
- ],
136
- );
137
-
138
- // Reset service (for reconnection, etc.)
139
- const resetService = useCallback(() => {
140
- serviceInstanceRef.current = null;
141
- setConnectionStatus("disconnected");
142
- setEventGroups([]);
143
- setTokenUsage(null);
144
- }, []);
145
-
146
- useEffect(() => {
147
- // Close existing connection when switching sessions,
148
- // but skip if the current service already owns this session
149
- // (happens when getAutoBeService just created it and set the URL).
150
- const prev = serviceInstanceRef.current;
151
- if (prev) {
152
- if (prev.sessionId === activeConversationId) {
153
- // Same session — do not tear down the connection we just created
154
- return;
155
- }
156
- void Promise.resolve(prev.close()).catch(() => {});
157
- serviceInstanceRef.current = null;
158
- setConnectionStatus("disconnected");
159
- }
160
-
161
- // Clear stale events immediately
162
- setEventGroups([]);
163
- setTokenUsage(null);
164
-
165
- if (activeConversationId === null) {
166
- return;
167
- }
168
-
169
- storageStrategy
170
- .getSession({
171
- id: activeConversationId,
172
- })
173
- .then((v) => {
174
- if (v === null) {
175
- return null;
176
- }
177
- refreshSessionList();
178
- // Avoid wiping live websocket replay events with empty storage data.
179
- // Some strategies only persist histories/token usage and leave events
180
- // empty, so replacing current groups here can erase already-delivered
181
- // replay messages such as early userMessage events.
182
- if (v.events.length > 0) {
183
- setEventGroups(v.events);
184
- }
185
- setTokenUsage(v.tokenUsage);
186
- })
187
- .catch(console.error);
188
- }, [activeConversationId]);
189
-
190
- // Register listener when service connects
191
- useEffect(() => {
192
- const instance = serviceInstanceRef.current;
193
- if (connectionStatus !== "connected" || instance === null) {
194
- return;
195
- }
196
-
197
- const onEvent = async (e: IAutoBeEventGroup[]) => {
198
- setEventGroups(e);
199
- setTimeout(() => {
200
- instance.service
201
- .getTokenUsage()
202
- .then(setTokenUsage)
203
- .catch(() => {});
204
- }, 0);
205
- };
206
-
207
- instance.listener.on(onEvent);
208
-
209
- instance.service
210
- .getTokenUsage()
211
- .then(setTokenUsage)
212
- .catch(() => {});
213
-
214
- return () => {
215
- instance.listener.off(onEvent);
216
- };
217
- }, [connectionStatus]);
218
-
219
- useEffect(() => {
220
- const instance = serviceInstanceRef.current;
221
- if (activeConversationId === null || connectionStatus !== "connected" || instance === null) {
222
- return;
223
- }
224
-
225
- const originConversate = instance.service.conversate;
226
- instance.service.conversate = async (content) => {
227
- const result = await originConversate(content);
228
- await storageStrategy.appendHistory({
229
- id: activeConversationId,
230
- history: result,
231
- });
232
- return result;
233
- };
234
-
235
- const registerEvent = async (e: IAutoBeEventGroup[]) => {
236
- await storageStrategy.appendEvent({
237
- id: activeConversationId,
238
- events: e,
239
- });
240
- await storageStrategy.setTokenUsage({
241
- id: activeConversationId,
242
- tokenUsage: await instance.service.getTokenUsage(),
243
- });
244
- };
245
-
246
- instance.listener.on(registerEvent);
247
- return () => {
248
- instance.service.conversate = originConversate;
249
- instance.listener.off(registerEvent);
250
- };
251
- }, [activeConversationId, connectionStatus]);
252
-
253
- const currentInstance = serviceInstanceRef.current;
254
-
255
- return (
256
- <AutoBeAgentContext.Provider
257
- value={{
258
- // Service state
259
- connectionStatus,
260
-
261
- // Service data
262
- eventGroups,
263
- tokenUsage,
264
- state: currentInstance?.listener?.getState() ?? null,
265
- service: currentInstance?.service ?? null,
266
- listener: currentInstance?.listener ?? null,
267
-
268
- // Service management
269
- getAutoBeService,
270
- resetService,
271
- }}
272
- >
273
- {children}
274
- </AutoBeAgentContext.Provider>
275
- );
276
- }
277
-
278
- export function useAutoBeAgent() {
279
- const context = useContext(AutoBeAgentContext);
280
- if (!context) {
281
- throw new Error("useAutoBeAgent must be used within a AutoBeAgentProvider");
282
- }
283
- return context;
284
- }
285
-
286
- /**
287
- * Wrap a TGrid driver Proxy into a plain object so that React dev-mode
288
- * render logging never touches the Proxy (which crashes on Symbol
289
- * property access: "Cannot convert a Symbol value to a string").
290
- */
291
- function wrapServiceProxy(
292
- service: IAutoBeRpcService,
293
- ): IAutoBeRpcService {
294
- return {
295
- conversate: (content) => service.conversate(content),
296
- getFiles: (options) => service.getFiles(options),
297
- getHistories: () => service.getHistories(),
298
- getTokenUsage: () => service.getTokenUsage(),
299
- getPhase: () => service.getPhase(),
300
- };
301
- }
1
+ import { IAutoBeRpcService, IAutoBeTokenUsageJson } from "@autobe/interface";
2
+ import {
3
+ ReactNode,
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useRef,
9
+ useState,
10
+ } from "react";
11
+
12
+ import {
13
+ AutoBeListener,
14
+ AutoBeListenerState,
15
+ IAutoBeAgentSessionStorageStrategy,
16
+ IAutoBeEventGroup,
17
+ } from "../structure";
18
+ import { IAutoBeConfig } from "../types/config";
19
+ import { useAutoBeAgentSessionList } from "./AutoBeAgentSessionList";
20
+ import { useSearchParams } from "./SearchParamsContext";
21
+
22
+ export interface IAutoBeServiceData {
23
+ service: IAutoBeRpcService;
24
+ listener: AutoBeListener;
25
+ close: () => void | Promise<void>;
26
+ sessionId: string;
27
+ }
28
+
29
+ export type AutoBeServiceFactory = (
30
+ config: IAutoBeConfig,
31
+ ) => Promise<IAutoBeServiceData>;
32
+
33
+ export type AutoBeConnectionStatus =
34
+ | "disconnected"
35
+ | "connecting"
36
+ | "connected";
37
+
38
+ interface AutoBeAgentContextType {
39
+ // Service state
40
+ connectionStatus: AutoBeConnectionStatus;
41
+
42
+ // Service data (available when ready)
43
+ eventGroups: IAutoBeEventGroup[];
44
+ tokenUsage: IAutoBeTokenUsageJson | null;
45
+ state: AutoBeListenerState | null;
46
+ service: IAutoBeRpcService | null;
47
+ listener: AutoBeListener | null;
48
+
49
+ // Service management
50
+ getAutoBeService: (config?: IAutoBeConfig) => Promise<IAutoBeServiceData>;
51
+ resetService: () => void;
52
+ }
53
+
54
+ const AutoBeAgentContext = createContext<AutoBeAgentContextType | null>(null);
55
+
56
+ export function AutoBeAgentProvider({
57
+ children,
58
+ serviceFactory,
59
+ storageStrategy,
60
+ }: {
61
+ serviceFactory: AutoBeServiceFactory;
62
+ children: ReactNode;
63
+ storageStrategy: IAutoBeAgentSessionStorageStrategy;
64
+ }) {
65
+ // Service state
66
+ const [connectionStatus, setConnectionStatus] =
67
+ useState<AutoBeConnectionStatus>("disconnected");
68
+
69
+ // Service data
70
+ const { searchParams, setSearchParams } = useSearchParams();
71
+ // Use URL parameter for conversation ID - enables bookmark/share support
72
+ const activeConversationId = searchParams.get("session-id") ?? null;
73
+
74
+ const [tokenUsage, setTokenUsage] = useState<IAutoBeTokenUsageJson | null>(
75
+ null,
76
+ );
77
+ const [eventGroups, setEventGroups] = useState<IAutoBeEventGroup[]>([]);
78
+
79
+ // Store service instance in a ref to avoid React dev mode inspecting
80
+ // the TGrid driver Proxy (which crashes on Symbol property access).
81
+ // connectionStatus state changes drive re-renders instead.
82
+ const serviceInstanceRef = useRef<IAutoBeServiceData | null>(null);
83
+
84
+ const { refreshSessionList } = useAutoBeAgentSessionList();
85
+ // Context-scoped service getter
86
+ const getAutoBeService = useCallback(
87
+ async (
88
+ config: IAutoBeConfig = {} as IAutoBeConfig,
89
+ ): Promise<IAutoBeServiceData> => {
90
+ // Return existing instance if available
91
+ if (serviceInstanceRef.current && connectionStatus === "connected") {
92
+ return serviceInstanceRef.current;
93
+ }
94
+
95
+ // Prevent multiple concurrent creations
96
+ if (connectionStatus === "connecting") {
97
+ throw new Error("Service is already connecting. Please wait.");
98
+ }
99
+
100
+ if (!serviceFactory) {
101
+ throw new Error("No service factory provided. Cannot create service.");
102
+ }
103
+
104
+ try {
105
+ setConnectionStatus("connecting");
106
+
107
+ // Create new service instance
108
+ const newServiceData = await serviceFactory({
109
+ ...config,
110
+ sessionId: activeConversationId,
111
+ });
112
+ // Wrap the TGrid driver proxy to handle Symbol access from
113
+ // React dev-mode's render logging without crashing.
114
+ newServiceData.service = wrapServiceProxy(newServiceData.service);
115
+ serviceInstanceRef.current = newServiceData;
116
+
117
+ setSearchParams((sp) => {
118
+ const newSp = new URLSearchParams(sp);
119
+ newSp.set("session-id", newServiceData.sessionId);
120
+ return newSp;
121
+ });
122
+ setConnectionStatus("connected");
123
+
124
+ return newServiceData;
125
+ } catch (error) {
126
+ setConnectionStatus("disconnected");
127
+ throw error;
128
+ }
129
+ },
130
+ [
131
+ serviceFactory,
132
+ connectionStatus,
133
+ activeConversationId,
134
+ searchParams,
135
+ ],
136
+ );
137
+
138
+ // Reset service (for reconnection, etc.)
139
+ const resetService = useCallback(() => {
140
+ serviceInstanceRef.current = null;
141
+ setConnectionStatus("disconnected");
142
+ setEventGroups([]);
143
+ setTokenUsage(null);
144
+ }, []);
145
+
146
+ useEffect(() => {
147
+ // Close existing connection when switching sessions,
148
+ // but skip if the current service already owns this session
149
+ // (happens when getAutoBeService just created it and set the URL).
150
+ const prev = serviceInstanceRef.current;
151
+ if (prev) {
152
+ if (prev.sessionId === activeConversationId) {
153
+ // Same session — do not tear down the connection we just created
154
+ return;
155
+ }
156
+ void Promise.resolve(prev.close()).catch(() => {});
157
+ serviceInstanceRef.current = null;
158
+ setConnectionStatus("disconnected");
159
+ }
160
+
161
+ // Clear stale events immediately
162
+ setEventGroups([]);
163
+ setTokenUsage(null);
164
+
165
+ if (activeConversationId === null) {
166
+ return;
167
+ }
168
+
169
+ storageStrategy
170
+ .getSession({
171
+ id: activeConversationId,
172
+ })
173
+ .then((v) => {
174
+ if (v === null) {
175
+ return null;
176
+ }
177
+ refreshSessionList();
178
+ // Avoid wiping live websocket replay events with empty storage data.
179
+ // Some strategies only persist histories/token usage and leave events
180
+ // empty, so replacing current groups here can erase already-delivered
181
+ // replay messages such as early userMessage events.
182
+ if (v.events.length > 0) {
183
+ setEventGroups(v.events);
184
+ }
185
+ setTokenUsage(v.tokenUsage);
186
+ })
187
+ .catch(console.error);
188
+ }, [activeConversationId]);
189
+
190
+ // Register listener when service connects
191
+ useEffect(() => {
192
+ const instance = serviceInstanceRef.current;
193
+ if (connectionStatus !== "connected" || instance === null) {
194
+ return;
195
+ }
196
+
197
+ const onEvent = async (e: IAutoBeEventGroup[]) => {
198
+ setEventGroups(e);
199
+ setTimeout(() => {
200
+ instance.service
201
+ .getTokenUsage()
202
+ .then(setTokenUsage)
203
+ .catch(() => {});
204
+ }, 0);
205
+ };
206
+
207
+ instance.listener.on(onEvent);
208
+
209
+ instance.service
210
+ .getTokenUsage()
211
+ .then(setTokenUsage)
212
+ .catch(() => {});
213
+
214
+ return () => {
215
+ instance.listener.off(onEvent);
216
+ };
217
+ }, [connectionStatus]);
218
+
219
+ useEffect(() => {
220
+ const instance = serviceInstanceRef.current;
221
+ if (activeConversationId === null || connectionStatus !== "connected" || instance === null) {
222
+ return;
223
+ }
224
+
225
+ const originConversate = instance.service.conversate;
226
+ instance.service.conversate = async (content) => {
227
+ const result = await originConversate(content);
228
+ await storageStrategy.appendHistory({
229
+ id: activeConversationId,
230
+ history: result,
231
+ });
232
+ return result;
233
+ };
234
+
235
+ const registerEvent = async (e: IAutoBeEventGroup[]) => {
236
+ await storageStrategy.appendEvent({
237
+ id: activeConversationId,
238
+ events: e,
239
+ });
240
+ await storageStrategy.setTokenUsage({
241
+ id: activeConversationId,
242
+ tokenUsage: await instance.service.getTokenUsage(),
243
+ });
244
+ };
245
+
246
+ instance.listener.on(registerEvent);
247
+ return () => {
248
+ instance.service.conversate = originConversate;
249
+ instance.listener.off(registerEvent);
250
+ };
251
+ }, [activeConversationId, connectionStatus]);
252
+
253
+ const currentInstance = serviceInstanceRef.current;
254
+
255
+ return (
256
+ <AutoBeAgentContext.Provider
257
+ value={{
258
+ // Service state
259
+ connectionStatus,
260
+
261
+ // Service data
262
+ eventGroups,
263
+ tokenUsage,
264
+ state: currentInstance?.listener?.getState() ?? null,
265
+ service: currentInstance?.service ?? null,
266
+ listener: currentInstance?.listener ?? null,
267
+
268
+ // Service management
269
+ getAutoBeService,
270
+ resetService,
271
+ }}
272
+ >
273
+ {children}
274
+ </AutoBeAgentContext.Provider>
275
+ );
276
+ }
277
+
278
+ export function useAutoBeAgent() {
279
+ const context = useContext(AutoBeAgentContext);
280
+ if (!context) {
281
+ throw new Error("useAutoBeAgent must be used within a AutoBeAgentProvider");
282
+ }
283
+ return context;
284
+ }
285
+
286
+ /**
287
+ * Wrap a TGrid driver Proxy into a plain object so that React dev-mode
288
+ * render logging never touches the Proxy (which crashes on Symbol
289
+ * property access: "Cannot convert a Symbol value to a string").
290
+ */
291
+ function wrapServiceProxy(
292
+ service: IAutoBeRpcService,
293
+ ): IAutoBeRpcService {
294
+ return {
295
+ conversate: (content) => service.conversate(content),
296
+ getFiles: (options) => service.getFiles(options),
297
+ getHistories: () => service.getHistories(),
298
+ getTokenUsage: () => service.getTokenUsage(),
299
+ getPhase: () => service.getPhase(),
300
+ };
301
+ }