@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,484 +1,486 @@
1
- import { IAutoBeTokenUsageJson } from "@autobe/interface";
2
- import React from "react";
3
-
4
- import { useAutoBeAgent } from "../context/AutoBeAgentContext";
5
- import { useEscapeKey } from "../hooks";
6
- import { AutoBeListenerState } from "../structure";
7
- import { toCompactNumberFormat } from "../utils";
8
-
9
- // Types
10
- interface IAutoBeStatusModalProps {
11
- isOpen: boolean;
12
- onClose: () => void;
13
- }
14
-
15
- // Style constants
16
- const MODAL_STYLES = {
17
- overlay: {
18
- position: "fixed" as const,
19
- top: 0,
20
- left: 0,
21
- right: 0,
22
- bottom: 0,
23
- background: "rgba(0, 0, 0, 0.6)",
24
- backdropFilter: "blur(8px)",
25
- display: "flex",
26
- alignItems: "center",
27
- justifyContent: "center",
28
- zIndex: 1001,
29
- padding: "1rem",
30
- },
31
- container: {
32
- background: "#ffffff",
33
- borderRadius: "16px",
34
- boxShadow:
35
- "0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.05)",
36
- maxWidth: "700px",
37
- maxHeight: "85vh",
38
- width: "100%",
39
- overflowY: "auto" as const,
40
- position: "relative" as const,
41
- },
42
- closeButton: {
43
- position: "absolute" as const,
44
- top: "1rem",
45
- right: "1rem",
46
- background: "rgba(107, 114, 128, 0.1)",
47
- border: "none",
48
- borderRadius: "50%",
49
- width: "2rem",
50
- height: "2rem",
51
- display: "flex",
52
- alignItems: "center",
53
- justifyContent: "center",
54
- cursor: "pointer",
55
- color: "#6b7280",
56
- fontSize: "1.2rem",
57
- zIndex: 10,
58
- },
59
- content: {
60
- padding: "1.25rem",
61
- },
62
- section: {
63
- display: "flex",
64
- flexDirection: "column" as const,
65
- gap: "1.5rem",
66
- },
67
- } as const;
68
-
69
- const CARD_STYLES = {
70
- base: {
71
- background: "linear-gradient(145deg, #ffffff, #f8fafc)",
72
- borderRadius: "8px",
73
- padding: "0.7rem",
74
- marginBottom: "0.4rem",
75
- border: "1px solid rgba(148, 163, 184, 0.15)",
76
- boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.1)",
77
- },
78
- header: {
79
- margin: "0 0 0.5rem 0",
80
- fontSize: "0.8rem",
81
- fontWeight: "700",
82
- color: "#374151",
83
- textTransform: "capitalize" as const,
84
- letterSpacing: "0.025em",
85
- },
86
- sectionTitle: {
87
- margin: 0,
88
- color: "#1e293b",
89
- fontSize: "0.9rem",
90
- fontWeight: "600",
91
- letterSpacing: "-0.01em",
92
- },
93
- } as const;
94
-
95
- const PROGRESS_STYLES = {
96
- card: {
97
- borderRadius: "10px",
98
- padding: "0.75rem",
99
- position: "relative" as const,
100
- overflow: "hidden",
101
- },
102
- completed: {
103
- background: "linear-gradient(135deg, #10b981, #059669)",
104
- border: "1px solid rgba(16, 185, 129, 0.2)",
105
- color: "white",
106
- boxShadow: "0 2px 8px rgba(16, 185, 129, 0.25)",
107
- },
108
- pending: {
109
- background: "linear-gradient(145deg, #ffffff, #f8fafc)",
110
- border: "1px solid rgba(148, 163, 184, 0.15)",
111
- color: "#6b7280",
112
- boxShadow: "0 2px 4px -1px rgba(0, 0, 0, 0.05)",
113
- },
114
- } as const;
115
-
116
- // Step configuration
117
- const PROGRESS_STEPS = [
118
- {
119
- name: "analyze",
120
- title: "Requirements Analysis",
121
- getResults: (state: AutoBeListenerState) => {
122
- if (!state.analyze) return null;
123
- const fileCount = Object.keys(state.analyze.files).length;
124
- const roleCount = state.analyze.actors.length;
125
- return `${fileCount} analysis files, ${roleCount} actors`;
126
- },
127
- },
128
- {
129
- name: "prisma",
130
- title: "Database Design",
131
- getResults: (state: AutoBeListenerState) => {
132
- if (!state.prisma) return null;
133
- const schemaCount = Object.keys(state.prisma.schemas).length;
134
- return `${schemaCount} schema files`;
135
- },
136
- },
137
- {
138
- name: "interface",
139
- title: "API Interface",
140
- getResults: (state: AutoBeListenerState) => {
141
- if (!state.interface) return null;
142
- const operationCount = state.interface.document.operations.length;
143
- const authCount = state.interface.authorizations.length;
144
- return `${operationCount} API endpoints, ${authCount} auth`;
145
- },
146
- },
147
- {
148
- name: "test",
149
- title: "Test Code",
150
- getResults: (state: AutoBeListenerState) => {
151
- if (!state.test) return null;
152
- const testCount = state.test.files.length;
153
- return `${testCount} test files`;
154
- },
155
- },
156
- {
157
- name: "realize",
158
- title: "Implementation Complete",
159
- getResults: (state: AutoBeListenerState) => {
160
- if (!state.realize) return null;
161
- const functionCount = state.realize.functions.length;
162
- const authCount = state.realize.authorizations.length;
163
- return `${functionCount} implementation functions, ${authCount} auth decorators`;
164
- },
165
- },
166
- ] as const;
167
-
168
- // Helper components
169
- const SectionTitle = ({ children }: { children: React.ReactNode }) => (
170
- <div style={{ marginBottom: "1rem" }}>
171
- <h3 style={CARD_STYLES.sectionTitle}>{children}</h3>
172
- </div>
173
- );
174
-
175
- // const ConnectionInfo = ({
176
- // header,
177
- // }: {
178
- // header: IAutoBePlaygroundHeader<ILlmSchema.Model> | null;
179
- // }) => (
180
- // <div>
181
- // <SectionTitle>Connection Info</SectionTitle>
182
- // <div
183
- // style={{
184
- // background: "linear-gradient(145deg, #ffffff, #f8fafc)",
185
- // borderRadius: "10px",
186
- // padding: "1rem",
187
- // border: "1px solid rgba(148, 163, 184, 0.15)",
188
- // boxShadow: "0 2px 4px -1px rgba(0, 0, 0, 0.05)",
189
- // }}
190
- // >
191
- // <p
192
- // style={{
193
- // margin: 0,
194
- // fontSize: "0.8rem",
195
- // color: "#374151",
196
- // lineHeight: 1.5,
197
- // }}
198
- // >
199
- // <strong>Model:</strong> {header?.model || "N/A"} |{" "}
200
- // <strong>Vendor:</strong> {header?.vendor.model || "N/A"} |{" "}
201
- // <strong>Locale:</strong> {header?.locale || "N/A"} |{" "}
202
- // <strong>Timezone:</strong> {header?.timezone || "N/A"}
203
- // </p>
204
- // </div>
205
- // </div>
206
- // );
207
-
208
- const HighlightNumbers = ({
209
- text,
210
- isCompleted,
211
- }: {
212
- text: string;
213
- isCompleted: boolean;
214
- }) => {
215
- const parts = text.split(/(\d+)/);
216
- return (
217
- <>
218
- {parts.map((part, index) => {
219
- const isNumber = /^\d+$/.test(part);
220
- if (isNumber) {
221
- return (
222
- <span
223
- key={index}
224
- style={{
225
- fontWeight: "700",
226
- fontSize: "0.8rem",
227
- color: isCompleted ? "#ffffff" : "#3b82f6",
228
- }}
229
- >
230
- {part}
231
- </span>
232
- );
233
- }
234
- return part;
235
- })}
236
- </>
237
- );
238
- };
239
-
240
- const ProgressCard = ({
241
- step,
242
- isCompleted,
243
- results,
244
- }: {
245
- step: (typeof PROGRESS_STEPS)[number];
246
- isCompleted: boolean;
247
- results: string | null;
248
- }) => (
249
- <div
250
- style={{
251
- ...PROGRESS_STYLES.card,
252
- ...(isCompleted ? PROGRESS_STYLES.completed : PROGRESS_STYLES.pending),
253
- }}
254
- >
255
- <div
256
- style={{
257
- display: "flex",
258
- justifyContent: "space-between",
259
- alignItems: "center",
260
- }}
261
- >
262
- <div style={{ flex: 1 }}>
263
- <div style={{ display: "flex", flexDirection: "row", gap: "0.2rem" }}>
264
- <span
265
- style={{
266
- fontSize: "0.85rem",
267
- fontWeight: "700",
268
- marginBottom: "0.2rem",
269
- textTransform: "uppercase",
270
- letterSpacing: "0.02em",
271
- }}
272
- >
273
- {step.name}
274
- </span>
275
- <span
276
- style={{
277
- fontSize: "0.75rem",
278
- opacity: 0.9,
279
- marginBottom: "0.2rem",
280
- }}
281
- >
282
- ({step.title})
283
- </span>
284
- </div>
285
-
286
- {results && (
287
- <div
288
- style={{
289
- fontSize: "0.7rem",
290
- opacity: 0.8,
291
- fontWeight: "500",
292
- }}
293
- >
294
- <HighlightNumbers text={results} isCompleted={isCompleted} />
295
- </div>
296
- )}
297
- </div>
298
- <div
299
- style={{
300
- fontSize: "0.7rem",
301
- fontWeight: "600",
302
- padding: "0.3rem 0.6rem",
303
- borderRadius: "10px",
304
- background: isCompleted
305
- ? "rgba(255, 255, 255, 0.2)"
306
- : "rgba(107, 114, 128, 0.1)",
307
- }}
308
- >
309
- {isCompleted ? "Completed" : "Pending"}
310
- </div>
311
- </div>
312
- </div>
313
- );
314
-
315
- const ProgressStatus = ({ state }: { state: AutoBeListenerState | null }) =>
316
- state && (
317
- <div>
318
- <SectionTitle>Progress Status</SectionTitle>
319
- <div
320
- style={{
321
- display: "flex",
322
- flexDirection: "column",
323
- gap: "0.5rem",
324
- }}
325
- >
326
- {PROGRESS_STEPS.map((step) => (
327
- <ProgressCard
328
- key={step.name}
329
- step={step}
330
- isCompleted={state[step.name as keyof AutoBeListenerState] !== null}
331
- results={step.getResults(state)}
332
- />
333
- ))}
334
- </div>
335
- </div>
336
- );
337
-
338
- const TokenRow = ({
339
- label,
340
- value,
341
- subInfo,
342
- }: {
343
- label: string;
344
- value: number;
345
- subInfo?: { label: string; value: number };
346
- }) => (
347
- <div
348
- style={{
349
- display: "flex",
350
- justifyContent: "space-between",
351
- alignItems: "center",
352
- }}
353
- >
354
- <div style={{ display: "flex", flexDirection: "column" }}>
355
- <span
356
- style={{
357
- fontSize: "0.7rem",
358
- fontWeight: "600",
359
- color: "#6b7280",
360
- }}
361
- >
362
- {label}
363
- </span>
364
- {subInfo && (
365
- <span
366
- style={{
367
- fontSize: "0.6rem",
368
- color: "#9ca3af",
369
- fontWeight: "500",
370
- }}
371
- >
372
- {subInfo.label}: {toCompactNumberFormat(subInfo.value)}
373
- </span>
374
- )}
375
- </div>
376
- <span
377
- style={{
378
- fontSize: "0.75rem",
379
- fontWeight: "700",
380
- color: "#1f2937",
381
- }}
382
- >
383
- {toCompactNumberFormat(value)}
384
- </span>
385
- </div>
386
- );
387
-
388
- const ComponentStats = ({
389
- name,
390
- component,
391
- }: {
392
- name: string;
393
- component: IAutoBeTokenUsageJson.IComponent;
394
- }) => (
395
- <div style={CARD_STYLES.base}>
396
- <h4 style={CARD_STYLES.header}>{name}</h4>
397
- <div
398
- style={{
399
- display: "flex",
400
- flexDirection: "column",
401
- gap: "0.4rem",
402
- }}
403
- >
404
- <div
405
- style={{
406
- display: "flex",
407
- flexDirection: "column",
408
- gap: "0.2rem",
409
- }}
410
- >
411
- <TokenRow label="Total" value={component.total} />
412
- <TokenRow
413
- label="Input"
414
- value={component.input.total}
415
- subInfo={
416
- component.input.cached > 0
417
- ? { label: "Cache", value: component.input.cached }
418
- : undefined
419
- }
420
- />
421
- <TokenRow label="Output" value={component.output.total} />
422
- </div>
423
- </div>
424
- </div>
425
- );
426
-
427
- const TokenUsage = ({
428
- tokenUsage,
429
- }: {
430
- tokenUsage: IAutoBeTokenUsageJson | null;
431
- }) => {
432
- if (!tokenUsage) {
433
- return (
434
- <div
435
- style={{
436
- padding: "2rem",
437
- textAlign: "center",
438
- color: "#6b7280",
439
- fontSize: "0.9rem",
440
- }}
441
- >
442
- Unable to load token usage information
443
- </div>
444
- );
445
- }
446
-
447
- return (
448
- <div>
449
- <SectionTitle>Token Usage - Total</SectionTitle>
450
- <ComponentStats name="total" component={tokenUsage.aggregate} />
451
- </div>
452
- );
453
- };
454
-
455
- export const AutoBeStatusModal = ({
456
- isOpen,
457
- onClose,
458
- }: IAutoBeStatusModalProps) => {
459
- const { tokenUsage, state } = useAutoBeAgent();
460
-
461
- useEscapeKey(isOpen, onClose);
462
-
463
- if (!isOpen) return null;
464
-
465
- return (
466
- <div style={MODAL_STYLES.overlay} onClick={onClose}>
467
- <div style={MODAL_STYLES.container} onClick={(e) => e.stopPropagation()}>
468
- <button onClick={onClose} style={MODAL_STYLES.closeButton}>
469
- ×
470
- </button>
471
-
472
- <div style={MODAL_STYLES.content}>
473
- <div style={MODAL_STYLES.section}>
474
- {/* <ConnectionInfo header={header} /> */}
475
- <ProgressStatus state={state} />
476
- <TokenUsage tokenUsage={tokenUsage} />
477
- </div>
478
- </div>
479
- </div>
480
- </div>
481
- );
482
- };
483
-
484
- export default AutoBeStatusModal;
1
+ import { IAutoBeTokenUsageJson } from "@autobe/interface";
2
+ import React from "react";
3
+
4
+ import { useAutoBeAgent } from "../context/AutoBeAgentContext";
5
+ import { useEscapeKey } from "../hooks";
6
+ import { AutoBeListenerState } from "../structure";
7
+ import { toCompactNumberFormat } from "../utils";
8
+
9
+ // Types
10
+ interface IAutoBeStatusModalProps {
11
+ isOpen: boolean;
12
+ onClose: () => void;
13
+ }
14
+
15
+ // Style constants
16
+ const MODAL_STYLES = {
17
+ overlay: {
18
+ position: "fixed" as const,
19
+ top: 0,
20
+ left: 0,
21
+ right: 0,
22
+ bottom: 0,
23
+ background: "rgba(0, 0, 0, 0.6)",
24
+ backdropFilter: "blur(8px)",
25
+ display: "flex",
26
+ alignItems: "center",
27
+ justifyContent: "center",
28
+ zIndex: 1001,
29
+ padding: "1rem",
30
+ },
31
+ container: {
32
+ background: "#ffffff",
33
+ borderRadius: "16px",
34
+ boxShadow:
35
+ "0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.05)",
36
+ maxWidth: "700px",
37
+ maxHeight: "85vh",
38
+ width: "100%",
39
+ overflowY: "auto" as const,
40
+ position: "relative" as const,
41
+ },
42
+ closeButton: {
43
+ position: "absolute" as const,
44
+ top: "1rem",
45
+ right: "1rem",
46
+ background: "rgba(107, 114, 128, 0.1)",
47
+ border: "none",
48
+ borderRadius: "50%",
49
+ width: "2rem",
50
+ height: "2rem",
51
+ display: "flex",
52
+ alignItems: "center",
53
+ justifyContent: "center",
54
+ cursor: "pointer",
55
+ color: "#6b7280",
56
+ fontSize: "1.2rem",
57
+ zIndex: 10,
58
+ },
59
+ content: {
60
+ padding: "1.25rem",
61
+ },
62
+ section: {
63
+ display: "flex",
64
+ flexDirection: "column" as const,
65
+ gap: "1.5rem",
66
+ },
67
+ } as const;
68
+
69
+ const CARD_STYLES = {
70
+ base: {
71
+ background: "linear-gradient(145deg, #ffffff, #f8fafc)",
72
+ borderRadius: "8px",
73
+ padding: "0.7rem",
74
+ marginBottom: "0.4rem",
75
+ border: "1px solid rgba(148, 163, 184, 0.15)",
76
+ boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.1)",
77
+ },
78
+ header: {
79
+ margin: "0 0 0.5rem 0",
80
+ fontSize: "0.8rem",
81
+ fontWeight: "700",
82
+ color: "#374151",
83
+ textTransform: "capitalize" as const,
84
+ letterSpacing: "0.025em",
85
+ },
86
+ sectionTitle: {
87
+ margin: 0,
88
+ color: "#1e293b",
89
+ fontSize: "0.9rem",
90
+ fontWeight: "600",
91
+ letterSpacing: "-0.01em",
92
+ },
93
+ } as const;
94
+
95
+ const PROGRESS_STYLES = {
96
+ card: {
97
+ borderRadius: "10px",
98
+ padding: "0.75rem",
99
+ position: "relative" as const,
100
+ overflow: "hidden",
101
+ },
102
+ completed: {
103
+ background: "linear-gradient(135deg, #10b981, #059669)",
104
+ border: "1px solid rgba(16, 185, 129, 0.2)",
105
+ color: "white",
106
+ boxShadow: "0 2px 8px rgba(16, 185, 129, 0.25)",
107
+ },
108
+ pending: {
109
+ background: "linear-gradient(145deg, #ffffff, #f8fafc)",
110
+ border: "1px solid rgba(148, 163, 184, 0.15)",
111
+ color: "#6b7280",
112
+ boxShadow: "0 2px 4px -1px rgba(0, 0, 0, 0.05)",
113
+ },
114
+ } as const;
115
+
116
+ // Step configuration
117
+ const PROGRESS_STEPS = [
118
+ {
119
+ name: "analyze",
120
+ title: "Requirements Analysis",
121
+ getResults: (state: AutoBeListenerState) => {
122
+ if (!state.analyze) return null;
123
+ const fileCount = Object.keys(state.analyze.files).length;
124
+ const roleCount = state.analyze.actors.length;
125
+ return `${fileCount} analysis files, ${roleCount} actors`;
126
+ },
127
+ },
128
+ {
129
+ name: "database",
130
+ title: "Database Design",
131
+ getResults: (state: AutoBeListenerState) => {
132
+ if (!state.database) return null;
133
+ const schemaCount = Object.keys(state.database.schemas).length;
134
+ return `${schemaCount} schema files`;
135
+ },
136
+ },
137
+ {
138
+ name: "interface",
139
+ title: "API Interface",
140
+ getResults: (state: AutoBeListenerState) => {
141
+ if (!state.interface) return null;
142
+ const operationCount = state.interface.document.operations.length;
143
+ const authCount = state.interface.authorizations.length;
144
+ return `${operationCount} API endpoints, ${authCount} auth`;
145
+ },
146
+ },
147
+ {
148
+ name: "test",
149
+ title: "Test Code",
150
+ getResults: (state: AutoBeListenerState) => {
151
+ if (!state.test) return null;
152
+ const testCount = state.test.functions.filter(
153
+ (f) => f.type === "operation",
154
+ ).length;
155
+ return `${testCount} test files`;
156
+ },
157
+ },
158
+ {
159
+ name: "realize",
160
+ title: "Implementation Complete",
161
+ getResults: (state: AutoBeListenerState) => {
162
+ if (!state.realize) return null;
163
+ const functionCount = state.realize.functions.length;
164
+ const authCount = state.realize.authorizations.length;
165
+ return `${functionCount} implementation functions, ${authCount} auth decorators`;
166
+ },
167
+ },
168
+ ] as const;
169
+
170
+ // Helper components
171
+ const SectionTitle = ({ children }: { children: React.ReactNode }) => (
172
+ <div style={{ marginBottom: "1rem" }}>
173
+ <h3 style={CARD_STYLES.sectionTitle}>{children}</h3>
174
+ </div>
175
+ );
176
+
177
+ // const ConnectionInfo = ({
178
+ // header,
179
+ // }: {
180
+ // header: IAutoBePlaygroundHeader<ILlmSchema.Model> | null;
181
+ // }) => (
182
+ // <div>
183
+ // <SectionTitle>Connection Info</SectionTitle>
184
+ // <div
185
+ // style={{
186
+ // background: "linear-gradient(145deg, #ffffff, #f8fafc)",
187
+ // borderRadius: "10px",
188
+ // padding: "1rem",
189
+ // border: "1px solid rgba(148, 163, 184, 0.15)",
190
+ // boxShadow: "0 2px 4px -1px rgba(0, 0, 0, 0.05)",
191
+ // }}
192
+ // >
193
+ // <p
194
+ // style={{
195
+ // margin: 0,
196
+ // fontSize: "0.8rem",
197
+ // color: "#374151",
198
+ // lineHeight: 1.5,
199
+ // }}
200
+ // >
201
+ // <strong>Model:</strong> {header?.model || "N/A"} |{" "}
202
+ // <strong>Vendor:</strong> {header?.vendor.model || "N/A"} |{" "}
203
+ // <strong>Locale:</strong> {header?.locale || "N/A"} |{" "}
204
+ // <strong>Timezone:</strong> {header?.timezone || "N/A"}
205
+ // </p>
206
+ // </div>
207
+ // </div>
208
+ // );
209
+
210
+ const HighlightNumbers = ({
211
+ text,
212
+ isCompleted,
213
+ }: {
214
+ text: string;
215
+ isCompleted: boolean;
216
+ }) => {
217
+ const parts = text.split(/(\d+)/);
218
+ return (
219
+ <>
220
+ {parts.map((part, index) => {
221
+ const isNumber = /^\d+$/.test(part);
222
+ if (isNumber) {
223
+ return (
224
+ <span
225
+ key={index}
226
+ style={{
227
+ fontWeight: "700",
228
+ fontSize: "0.8rem",
229
+ color: isCompleted ? "#ffffff" : "#3b82f6",
230
+ }}
231
+ >
232
+ {part}
233
+ </span>
234
+ );
235
+ }
236
+ return part;
237
+ })}
238
+ </>
239
+ );
240
+ };
241
+
242
+ const ProgressCard = ({
243
+ step,
244
+ isCompleted,
245
+ results,
246
+ }: {
247
+ step: (typeof PROGRESS_STEPS)[number];
248
+ isCompleted: boolean;
249
+ results: string | null;
250
+ }) => (
251
+ <div
252
+ style={{
253
+ ...PROGRESS_STYLES.card,
254
+ ...(isCompleted ? PROGRESS_STYLES.completed : PROGRESS_STYLES.pending),
255
+ }}
256
+ >
257
+ <div
258
+ style={{
259
+ display: "flex",
260
+ justifyContent: "space-between",
261
+ alignItems: "center",
262
+ }}
263
+ >
264
+ <div style={{ flex: 1 }}>
265
+ <div style={{ display: "flex", flexDirection: "row", gap: "0.2rem" }}>
266
+ <span
267
+ style={{
268
+ fontSize: "0.85rem",
269
+ fontWeight: "700",
270
+ marginBottom: "0.2rem",
271
+ textTransform: "uppercase",
272
+ letterSpacing: "0.02em",
273
+ }}
274
+ >
275
+ {step.name}
276
+ </span>
277
+ <span
278
+ style={{
279
+ fontSize: "0.75rem",
280
+ opacity: 0.9,
281
+ marginBottom: "0.2rem",
282
+ }}
283
+ >
284
+ ({step.title})
285
+ </span>
286
+ </div>
287
+
288
+ {results && (
289
+ <div
290
+ style={{
291
+ fontSize: "0.7rem",
292
+ opacity: 0.8,
293
+ fontWeight: "500",
294
+ }}
295
+ >
296
+ <HighlightNumbers text={results} isCompleted={isCompleted} />
297
+ </div>
298
+ )}
299
+ </div>
300
+ <div
301
+ style={{
302
+ fontSize: "0.7rem",
303
+ fontWeight: "600",
304
+ padding: "0.3rem 0.6rem",
305
+ borderRadius: "10px",
306
+ background: isCompleted
307
+ ? "rgba(255, 255, 255, 0.2)"
308
+ : "rgba(107, 114, 128, 0.1)",
309
+ }}
310
+ >
311
+ {isCompleted ? "Completed" : "Pending"}
312
+ </div>
313
+ </div>
314
+ </div>
315
+ );
316
+
317
+ const ProgressStatus = ({ state }: { state: AutoBeListenerState | null }) =>
318
+ state && (
319
+ <div>
320
+ <SectionTitle>Progress Status</SectionTitle>
321
+ <div
322
+ style={{
323
+ display: "flex",
324
+ flexDirection: "column",
325
+ gap: "0.5rem",
326
+ }}
327
+ >
328
+ {PROGRESS_STEPS.map((step) => (
329
+ <ProgressCard
330
+ key={step.name}
331
+ step={step}
332
+ isCompleted={state[step.name as keyof AutoBeListenerState] !== null}
333
+ results={step.getResults(state)}
334
+ />
335
+ ))}
336
+ </div>
337
+ </div>
338
+ );
339
+
340
+ const TokenRow = ({
341
+ label,
342
+ value,
343
+ subInfo,
344
+ }: {
345
+ label: string;
346
+ value: number;
347
+ subInfo?: { label: string; value: number };
348
+ }) => (
349
+ <div
350
+ style={{
351
+ display: "flex",
352
+ justifyContent: "space-between",
353
+ alignItems: "center",
354
+ }}
355
+ >
356
+ <div style={{ display: "flex", flexDirection: "column" }}>
357
+ <span
358
+ style={{
359
+ fontSize: "0.7rem",
360
+ fontWeight: "600",
361
+ color: "#6b7280",
362
+ }}
363
+ >
364
+ {label}
365
+ </span>
366
+ {subInfo && (
367
+ <span
368
+ style={{
369
+ fontSize: "0.6rem",
370
+ color: "#9ca3af",
371
+ fontWeight: "500",
372
+ }}
373
+ >
374
+ {subInfo.label}: {toCompactNumberFormat(subInfo.value)}
375
+ </span>
376
+ )}
377
+ </div>
378
+ <span
379
+ style={{
380
+ fontSize: "0.75rem",
381
+ fontWeight: "700",
382
+ color: "#1f2937",
383
+ }}
384
+ >
385
+ {toCompactNumberFormat(value)}
386
+ </span>
387
+ </div>
388
+ );
389
+
390
+ const ComponentStats = ({
391
+ name,
392
+ component,
393
+ }: {
394
+ name: string;
395
+ component: IAutoBeTokenUsageJson.IComponent;
396
+ }) => (
397
+ <div style={CARD_STYLES.base}>
398
+ <h4 style={CARD_STYLES.header}>{name}</h4>
399
+ <div
400
+ style={{
401
+ display: "flex",
402
+ flexDirection: "column",
403
+ gap: "0.4rem",
404
+ }}
405
+ >
406
+ <div
407
+ style={{
408
+ display: "flex",
409
+ flexDirection: "column",
410
+ gap: "0.2rem",
411
+ }}
412
+ >
413
+ <TokenRow label="Total" value={component.total} />
414
+ <TokenRow
415
+ label="Input"
416
+ value={component.input.total}
417
+ subInfo={
418
+ component.input.cached > 0
419
+ ? { label: "Cache", value: component.input.cached }
420
+ : undefined
421
+ }
422
+ />
423
+ <TokenRow label="Output" value={component.output.total} />
424
+ </div>
425
+ </div>
426
+ </div>
427
+ );
428
+
429
+ const TokenUsage = ({
430
+ tokenUsage,
431
+ }: {
432
+ tokenUsage: IAutoBeTokenUsageJson | null;
433
+ }) => {
434
+ if (!tokenUsage) {
435
+ return (
436
+ <div
437
+ style={{
438
+ padding: "2rem",
439
+ textAlign: "center",
440
+ color: "#6b7280",
441
+ fontSize: "0.9rem",
442
+ }}
443
+ >
444
+ Unable to load token usage information
445
+ </div>
446
+ );
447
+ }
448
+
449
+ return (
450
+ <div>
451
+ <SectionTitle>Token Usage - Total</SectionTitle>
452
+ <ComponentStats name="total" component={tokenUsage.aggregate} />
453
+ </div>
454
+ );
455
+ };
456
+
457
+ export const AutoBeStatusModal = ({
458
+ isOpen,
459
+ onClose,
460
+ }: IAutoBeStatusModalProps) => {
461
+ const { tokenUsage, state } = useAutoBeAgent();
462
+
463
+ useEscapeKey(isOpen, onClose);
464
+
465
+ if (!isOpen) return null;
466
+
467
+ return (
468
+ <div style={MODAL_STYLES.overlay} onClick={onClose}>
469
+ <div style={MODAL_STYLES.container} onClick={(e) => e.stopPropagation()}>
470
+ <button onClick={onClose} style={MODAL_STYLES.closeButton}>
471
+ ×
472
+ </button>
473
+
474
+ <div style={MODAL_STYLES.content}>
475
+ <div style={MODAL_STYLES.section}>
476
+ {/* <ConnectionInfo header={header} /> */}
477
+ <ProgressStatus state={state} />
478
+ <TokenUsage tokenUsage={tokenUsage} />
479
+ </div>
480
+ </div>
481
+ </div>
482
+ </div>
483
+ );
484
+ };
485
+
486
+ export default AutoBeStatusModal;