@autobe/ui 0.20.0 → 0.21.0

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 (115) hide show
  1. package/lib/AutoBeChatMain.d.ts +16 -0
  2. package/lib/AutoBeChatMain.js +51 -0
  3. package/lib/AutoBeChatMain.js.map +1 -0
  4. package/lib/banner/AutoBeAgentInformation.d.ts +15 -0
  5. package/lib/banner/AutoBeAgentInformation.js +41 -0
  6. package/lib/banner/AutoBeAgentInformation.js.map +1 -0
  7. package/lib/banner/AutoBeChatBanner.d.ts +15 -0
  8. package/lib/banner/AutoBeChatBanner.js +29 -0
  9. package/lib/banner/AutoBeChatBanner.js.map +1 -0
  10. package/lib/banner/AutoBeChatState.d.ts +6 -0
  11. package/lib/banner/AutoBeChatState.js +80 -0
  12. package/lib/banner/AutoBeChatState.js.map +1 -0
  13. package/lib/banner/AutoBeTokenUsage.d.ts +14 -0
  14. package/lib/banner/AutoBeTokenUsage.js +60 -0
  15. package/lib/banner/AutoBeTokenUsage.js.map +1 -0
  16. package/lib/banner/index.d.ts +4 -0
  17. package/lib/banner/index.js +21 -0
  18. package/lib/banner/index.js.map +1 -0
  19. package/lib/common/Collapsible.d.ts +15 -0
  20. package/lib/common/Collapsible.js +45 -0
  21. package/lib/common/Collapsible.js.map +1 -0
  22. package/lib/common/index.d.ts +2 -0
  23. package/lib/common/index.js +19 -0
  24. package/lib/common/index.js.map +1 -0
  25. package/lib/constant/color.d.ts +18 -0
  26. package/lib/constant/color.js +25 -0
  27. package/lib/constant/color.js.map +1 -0
  28. package/lib/events/AutoBeEventMovie.d.ts +8 -0
  29. package/lib/events/AutoBeEventMovie.js +84 -0
  30. package/lib/events/AutoBeEventMovie.js.map +1 -0
  31. package/lib/events/index.d.ts +2 -1
  32. package/lib/events/index.js +5 -3
  33. package/lib/events/index.js.map +1 -1
  34. package/lib/hooks/index.d.ts +2 -0
  35. package/lib/hooks/index.js +19 -0
  36. package/lib/hooks/index.js.map +1 -0
  37. package/lib/hooks/useIsomorphicLayoutEffect.d.ts +6 -0
  38. package/lib/hooks/useIsomorphicLayoutEffect.js +10 -0
  39. package/lib/hooks/useIsomorphicLayoutEffect.js.map +1 -0
  40. package/lib/hooks/useMediaQuery.d.ts +11 -0
  41. package/lib/hooks/useMediaQuery.js +52 -0
  42. package/lib/hooks/useMediaQuery.js.map +1 -0
  43. package/lib/index.d.ts +5 -3
  44. package/lib/index.js +8 -7
  45. package/lib/index.js.map +1 -1
  46. package/lib/structure/AutoBeListener.d.ts +17 -0
  47. package/lib/structure/AutoBeListener.js +250 -0
  48. package/lib/structure/AutoBeListener.js.map +1 -0
  49. package/lib/structure/AutoBeListenerState.d.ts +14 -0
  50. package/lib/structure/AutoBeListenerState.js +39 -0
  51. package/lib/structure/AutoBeListenerState.js.map +1 -0
  52. package/lib/structure/IAutoBeEventGroup.d.ts +5 -0
  53. package/lib/structure/IAutoBeEventGroup.js +3 -0
  54. package/lib/structure/IAutoBeEventGroup.js.map +1 -0
  55. package/lib/structure/index.d.ts +3 -0
  56. package/lib/structure/index.js +20 -0
  57. package/lib/structure/index.js.map +1 -0
  58. package/lib/upload/AutoBeChatUploadBox.d.ts +31 -0
  59. package/lib/upload/AutoBeChatUploadBox.js +221 -0
  60. package/lib/upload/AutoBeChatUploadBox.js.map +1 -0
  61. package/lib/upload/AutoBeChatUploadSendButton.js.map +1 -0
  62. package/lib/upload/AutoBeFileUploadBox.d.ts +8 -0
  63. package/lib/upload/AutoBeFileUploadBox.js.map +1 -0
  64. package/lib/upload/AutoBeUploadConfig.d.ts +9 -0
  65. package/lib/upload/AutoBeUploadConfig.js +3 -0
  66. package/lib/upload/AutoBeUploadConfig.js.map +1 -0
  67. package/lib/{AutoBeVoiceRecoderButton.js → upload/AutoBeVoiceRecoderButton.js} +2 -2
  68. package/lib/upload/AutoBeVoiceRecoderButton.js.map +1 -0
  69. package/lib/upload/index.d.ts +5 -0
  70. package/lib/upload/index.js +22 -0
  71. package/lib/upload/index.js.map +1 -0
  72. package/lib/utils/index.d.ts +1 -0
  73. package/lib/utils/index.js +1 -0
  74. package/lib/utils/index.js.map +1 -1
  75. package/lib/utils/number.d.ts +1 -0
  76. package/lib/utils/number.js +20 -0
  77. package/lib/utils/number.js.map +1 -0
  78. package/package.json +6 -10
  79. package/src/AutoBeChatMain.tsx +123 -0
  80. package/src/banner/AutoBeAgentInformation.tsx +102 -0
  81. package/src/banner/AutoBeChatBanner.tsx +72 -0
  82. package/src/banner/AutoBeChatState.tsx +152 -0
  83. package/src/banner/AutoBeTokenUsage.tsx +152 -0
  84. package/src/banner/index.ts +4 -0
  85. package/src/common/Collapsible.tsx +95 -0
  86. package/src/common/index.ts +2 -0
  87. package/src/constant/color.ts +24 -0
  88. package/src/events/AutoBeEventMovie.tsx +114 -0
  89. package/src/events/README.md +150 -19
  90. package/src/events/index.ts +2 -4
  91. package/src/hooks/index.ts +2 -0
  92. package/src/hooks/useIsomorphicLayoutEffect.ts +8 -0
  93. package/src/hooks/useMediaQuery.ts +68 -0
  94. package/src/index.ts +5 -6
  95. package/src/structure/AutoBeListener.ts +263 -0
  96. package/src/structure/AutoBeListenerState.ts +53 -0
  97. package/src/structure/IAutoBeEventGroup.ts +6 -0
  98. package/src/structure/index.ts +3 -0
  99. package/src/upload/AutoBeChatUploadBox.tsx +372 -0
  100. package/src/{AutoBeFileUploadBox.tsx → upload/AutoBeFileUploadBox.tsx} +7 -8
  101. package/src/upload/AutoBeUploadConfig.ts +5 -0
  102. package/src/{AutoBeVoiceRecoderButton.tsx → upload/AutoBeVoiceRecoderButton.tsx} +2 -2
  103. package/src/upload/index.ts +5 -0
  104. package/src/utils/index.ts +1 -0
  105. package/src/utils/number.ts +17 -0
  106. package/tsconfig.json +2 -1
  107. package/lib/AutoBeChatUploadSendButton.js.map +0 -1
  108. package/lib/AutoBeFileUploadBox.d.ts +0 -10
  109. package/lib/AutoBeFileUploadBox.js.map +0 -1
  110. package/lib/AutoBeVoiceRecoderButton.js.map +0 -1
  111. /package/lib/{AutoBeChatUploadSendButton.d.ts → upload/AutoBeChatUploadSendButton.d.ts} +0 -0
  112. /package/lib/{AutoBeChatUploadSendButton.js → upload/AutoBeChatUploadSendButton.js} +0 -0
  113. /package/lib/{AutoBeFileUploadBox.js → upload/AutoBeFileUploadBox.js} +0 -0
  114. /package/lib/{AutoBeVoiceRecoderButton.d.ts → upload/AutoBeVoiceRecoderButton.d.ts} +0 -0
  115. /package/src/{AutoBeChatUploadSendButton.tsx → upload/AutoBeChatUploadSendButton.tsx} +0 -0
@@ -0,0 +1,123 @@
1
+ import {
2
+ AutoBeUserMessageContent,
3
+ IAutoBePlaygroundHeader,
4
+ IAutoBeRpcService,
5
+ IAutoBeTokenUsageJson,
6
+ } from "@autobe/interface";
7
+ import { ILlmSchema } from "@samchon/openapi";
8
+ import { RefObject, useEffect, useRef } from "react";
9
+
10
+ import {
11
+ AutoBeChatBanner,
12
+ AutoBeChatUploadBox,
13
+ AutoBeEventMovie,
14
+ AutoBeListenerState,
15
+ IAutoBeEventGroup,
16
+ IAutoBeUploadConfig,
17
+ } from ".";
18
+ import { useMediaQuery } from "./hooks";
19
+
20
+ export interface IAutoBeChatMainProps {
21
+ isMobile: boolean;
22
+ eventGroups: IAutoBeEventGroup[];
23
+ service: IAutoBeRpcService;
24
+ conversate: (messages: AutoBeUserMessageContent[]) => Promise<void>;
25
+ setError: (error: Error) => void;
26
+ uploadConfig?: IAutoBeUploadConfig;
27
+ tokenUsage: IAutoBeTokenUsageJson | null;
28
+ header: IAutoBePlaygroundHeader<ILlmSchema.Model>;
29
+ state: AutoBeListenerState;
30
+ }
31
+
32
+ export const AutoBeChatMain = (props: IAutoBeChatMainProps) => {
33
+ const bodyContainerRef = useRef<HTMLDivElement>(null);
34
+ const scrollAnchorRef = useRef<HTMLDivElement>(null);
35
+
36
+ const listener: RefObject<AutoBeChatUploadBox.IListener> = useRef({
37
+ handleDragEnter: () => {},
38
+ handleDragLeave: () => {},
39
+ handleDrop: () => {},
40
+ handleDragOver: () => {},
41
+ });
42
+
43
+ useEffect(() => {
44
+ if (props.eventGroups.length === 0) return;
45
+ scrollAnchorRef.current?.scrollIntoView({
46
+ behavior: "smooth",
47
+ });
48
+ }, [bodyContainerRef.current?.scrollHeight]);
49
+
50
+ const isMinWidthLg = useMediaQuery(useMediaQuery.MIN_WIDTH_LG);
51
+ return (
52
+ <div
53
+ onDragEnter={(e) => listener.current.handleDragEnter(e)}
54
+ onDragLeave={(e) => listener.current.handleDragLeave(e)}
55
+ onDragOver={(e) => listener.current.handleDragOver(e)}
56
+ onDrop={(e) => listener.current.handleDrop(e)}
57
+ style={{
58
+ position: "relative",
59
+ overflowY: "auto",
60
+ margin: 0,
61
+ backgroundColor: "lightblue",
62
+ flexGrow: 1,
63
+ display: "flex",
64
+ flexDirection: "column",
65
+ }}
66
+ ref={bodyContainerRef}
67
+ >
68
+ {!isMinWidthLg && (
69
+ <AutoBeChatBanner
70
+ header={props.header}
71
+ tokenUsage={props.tokenUsage}
72
+ state={props.state}
73
+ />
74
+ )}
75
+
76
+ <div
77
+ style={{
78
+ backgroundColor: "lightblue",
79
+ padding: "2rem",
80
+ gap: 16,
81
+ display: "flex",
82
+ flexDirection: "column",
83
+ }}
84
+ >
85
+ {props.eventGroups.map((e, index) => (
86
+ <AutoBeEventMovie
87
+ key={index}
88
+ getFiles={props.service.getFiles}
89
+ events={e.events}
90
+ last={index === props.eventGroups.length - 1}
91
+ />
92
+ ))}
93
+ </div>
94
+
95
+ {/*
96
+ * Prompt input area
97
+ * this flexGrow: 1 means that the prompt input area will take up the remaining space
98
+ * so that the upload box will be at the bottom of the screen
99
+ */}
100
+ <div
101
+ style={{ flexGrow: 1, minHeight: "1rem" }}
102
+ ref={scrollAnchorRef}
103
+ ></div>
104
+ <div
105
+ style={{
106
+ position: "sticky",
107
+ bottom: 16,
108
+ left: 0,
109
+ right: 0,
110
+ zIndex: 1000,
111
+ }}
112
+ >
113
+ <AutoBeChatUploadBox
114
+ listener={listener}
115
+ uploadConfig={props.uploadConfig}
116
+ conversate={props.conversate}
117
+ setError={props.setError}
118
+ />
119
+ </div>
120
+ </div>
121
+ );
122
+ };
123
+ export default AutoBeChatMain;
@@ -0,0 +1,102 @@
1
+ import {
2
+ IAutoBePlaygroundHeader,
3
+ IAutoBePlaygroundVendor,
4
+ } from "@autobe/interface";
5
+ import { ILlmSchema } from "@samchon/openapi";
6
+ import { ReactNode } from "react";
7
+
8
+ import { COLORS } from "../constant/color";
9
+
10
+ export interface IAutoBeAgentInformationProps {
11
+ header: Omit<IAutoBePlaygroundHeader<ILlmSchema.Model>, "vendor"> & {
12
+ vendor: Omit<IAutoBePlaygroundVendor, "apiKey">;
13
+ };
14
+ }
15
+
16
+ /** Props interface for InfoRow component */
17
+ interface IInfoRowProps {
18
+ /** Label text */
19
+ label: string;
20
+ /** Value to display */
21
+ value: ReactNode;
22
+ }
23
+
24
+ /** Props interface for InfoLabel component */
25
+ interface IInfoLabelProps {
26
+ /** Label text */
27
+ children: string;
28
+ }
29
+
30
+ /** Props interface for InfoValue component */
31
+ interface IInfoValueProps {
32
+ /** Value content */
33
+ children: ReactNode;
34
+ }
35
+
36
+ /** Info row component with consistent flex layout */
37
+ const InfoRow = ({ label, value }: IInfoRowProps) => (
38
+ <div
39
+ style={{
40
+ display: "flex",
41
+ alignItems: "center",
42
+ padding: "4px 0",
43
+ gap: "8px",
44
+ }}
45
+ >
46
+ <InfoLabel>{label + ":"}</InfoLabel>
47
+ <InfoValue>{value}</InfoValue>
48
+ </div>
49
+ );
50
+
51
+ /** Info label component with consistent styling */
52
+ const InfoLabel = ({ children }: IInfoLabelProps) => (
53
+ <span
54
+ style={{
55
+ color: COLORS.GRAY_TEXT_MEDIUM,
56
+ fontWeight: "500",
57
+ fontSize: "0.875rem",
58
+ }}
59
+ >
60
+ {children}
61
+ </span>
62
+ );
63
+
64
+ /** Info value component with styling */
65
+ const InfoValue = ({ children }: IInfoValueProps) => (
66
+ <span
67
+ style={{
68
+ color: COLORS.GRAY_TEXT_DARK,
69
+ fontWeight: "600",
70
+ fontSize: "0.875rem",
71
+ }}
72
+ >
73
+ {children}
74
+ </span>
75
+ );
76
+
77
+ /**
78
+ * Agent information component displaying model, locale, and configuration
79
+ * details
80
+ *
81
+ * @param props - Component props
82
+ * @returns JSX element representing the agent information
83
+ */
84
+ export const AutoBeAgentInformation = ({
85
+ header,
86
+ }: IAutoBeAgentInformationProps) => {
87
+ return (
88
+ <div
89
+ style={{
90
+ display: "flex",
91
+ flexDirection: "column",
92
+ gap: "6px",
93
+ }}
94
+ >
95
+ <InfoRow label="AI Model" value={header.vendor.model} />
96
+ <InfoRow label="Schema Model" value={header.model} />
97
+ <InfoRow label="Locale" value={header.locale} />
98
+ <InfoRow label="Timezone" value={header.timezone} />
99
+ <InfoRow label="Semaphore" value={header.vendor.semaphore ?? 16} />
100
+ </div>
101
+ );
102
+ };
@@ -0,0 +1,72 @@
1
+ import { IAutoBeTokenUsageJson } from "@autobe/interface";
2
+
3
+ import { Collapsible } from "../common";
4
+ import { COLORS, SHADOWS } from "../constant/color";
5
+ import {
6
+ AutoBeAgentInformation,
7
+ IAutoBeAgentInformationProps,
8
+ } from "./AutoBeAgentInformation";
9
+ import { AutoBeChatState, IAutoBeChatStateProps } from "./AutoBeChatState";
10
+ import { AutoBeTokenUsage } from "./AutoBeTokenUsage";
11
+
12
+ /** Props interface for AutoBeChatBanner component */
13
+ interface IAutoBeChatBannerProps {
14
+ /** Agent information to display */
15
+ header: IAutoBeAgentInformationProps["header"];
16
+
17
+ /** Token usage data to display */
18
+ tokenUsage: IAutoBeTokenUsageJson | null;
19
+
20
+ /** Chat state to display */
21
+ state: IAutoBeChatStateProps["state"];
22
+ }
23
+
24
+ /** Chat banner component with collapsible token usage display */
25
+ export const AutoBeChatBanner = (props: IAutoBeChatBannerProps) => {
26
+ return (
27
+ <header
28
+ style={{
29
+ padding: "0 48",
30
+ position: "sticky",
31
+ top: "12px",
32
+ marginBottom: "1rem",
33
+ zIndex: 10,
34
+ }}
35
+ >
36
+ <div
37
+ style={{
38
+ border: `1px solid ${COLORS.GRAY_BORDER}`,
39
+ borderRadius: "8px",
40
+ backgroundColor: COLORS.GRAY_BACKGROUND,
41
+ padding: "0 16px 16px 16px",
42
+ boxShadow: SHADOWS.CARD,
43
+ width: "100%",
44
+ }}
45
+ >
46
+ <h3>Summaries</h3>
47
+
48
+ <Collapsible
49
+ title="Agent Information"
50
+ defaultCollapsed={true}
51
+ animated={true}
52
+ >
53
+ <AutoBeAgentInformation header={props.header} />
54
+ </Collapsible>
55
+ <br />
56
+ <Collapsible
57
+ title="Token Usage"
58
+ defaultCollapsed={false}
59
+ animated={true}
60
+ >
61
+ <AutoBeTokenUsage tokenUsage={props.tokenUsage} />
62
+ </Collapsible>
63
+ <br />
64
+ <Collapsible title="Chat State" defaultCollapsed={true} animated={true}>
65
+ <AutoBeChatState state={props.state} />
66
+ </Collapsible>
67
+ </div>
68
+ </header>
69
+ );
70
+ };
71
+
72
+ export default AutoBeChatBanner;
@@ -0,0 +1,152 @@
1
+ import {
2
+ AutoBeAnalyzeCompleteEvent,
3
+ AutoBeInterfaceCompleteEvent,
4
+ AutoBePrismaCompleteEvent,
5
+ AutoBeRealizeCompleteEvent,
6
+ AutoBeTestCompleteEvent,
7
+ } from "@autobe/interface";
8
+
9
+ import { AutoBeListenerState } from "..";
10
+ import { COLORS } from "../constant/color";
11
+
12
+ export interface IAutoBeChatStateProps {
13
+ state: AutoBeListenerState;
14
+ }
15
+
16
+ /** Common styles for step items */
17
+ const getStepItemStyle = (isActive: boolean): React.CSSProperties => ({
18
+ padding: "12px",
19
+ backgroundColor: COLORS.GRAY_BACKGROUND,
20
+ borderRadius: "6px",
21
+ borderLeft: `4px solid ${isActive ? "#007bff" : COLORS.GRAY_BORDER}`,
22
+ });
23
+
24
+ const stepTitleStyle: React.CSSProperties = {
25
+ fontWeight: "bold",
26
+ fontSize: "0.9rem",
27
+ marginBottom: "4px",
28
+ color: COLORS.GRAY_TEXT_DARK,
29
+ };
30
+
31
+ const stepDataStyle: React.CSSProperties = {
32
+ fontSize: "0.85rem",
33
+ color: COLORS.GRAY_TEXT_MEDIUM,
34
+ lineHeight: "1.4",
35
+ };
36
+
37
+ /** Props interface for StateStep component */
38
+ interface IStateStepProps {
39
+ /** Step name */
40
+ step: string;
41
+
42
+ data:
43
+ | AutoBeAnalyzeCompleteEvent
44
+ | AutoBePrismaCompleteEvent
45
+ | AutoBeInterfaceCompleteEvent
46
+ | AutoBeTestCompleteEvent
47
+ | AutoBeRealizeCompleteEvent;
48
+ }
49
+
50
+ /** Component for displaying active state step */
51
+ const StateStep = ({ step, data }: IStateStepProps) => (
52
+ <div style={getStepItemStyle(true)}>
53
+ <div
54
+ style={stepTitleStyle}
55
+ >{`${step.charAt(0).toUpperCase()}${step.slice(1)}`}</div>
56
+ <div style={stepDataStyle}>
57
+ {Object.entries(getStepCount(data))
58
+ .map(([key, value]) => `${key}: ${value.toLocaleString()}`)
59
+ .join(" • ")}
60
+ </div>
61
+ <div style={stepDataStyle}>
62
+ ⏱️ {(Math.floor((data.elapsed / 60_000) * 100) / 100).toLocaleString()}{" "}
63
+ mins
64
+ </div>
65
+ </div>
66
+ );
67
+
68
+ /** Component for displaying empty state step */
69
+ const StateEmpty = ({ step }: { step: string }) => (
70
+ <div style={getStepItemStyle(false)}>
71
+ <div
72
+ style={stepTitleStyle}
73
+ >{`${step.charAt(0).toUpperCase()}${step.slice(1)}`}</div>
74
+ <div style={stepDataStyle}>0 items • ⏱️ 0 mins</div>
75
+ </div>
76
+ );
77
+
78
+ /** Component to display development state information */
79
+ export const AutoBeChatState = (props: IAutoBeChatStateProps) => {
80
+ const containerStyle: React.CSSProperties = {
81
+ display: "flex",
82
+ flexDirection: "column",
83
+ gap: "12px",
84
+ };
85
+
86
+ const steps = ["analyze", "prisma", "interface", "test", "realize"] as const;
87
+
88
+ return (
89
+ <div style={containerStyle}>
90
+ <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
91
+ {steps.map((stepType) =>
92
+ props.state[stepType] ? (
93
+ <StateStep
94
+ key={stepType}
95
+ step={stepType}
96
+ data={props.state[stepType]}
97
+ />
98
+ ) : (
99
+ <StateEmpty key={stepType} step={stepType} />
100
+ ),
101
+ )}
102
+ </div>
103
+ </div>
104
+ );
105
+ };
106
+
107
+ /** Calculate count data based on step type */
108
+ const getStepCount = (
109
+ data:
110
+ | AutoBeAnalyzeCompleteEvent
111
+ | AutoBePrismaCompleteEvent
112
+ | AutoBeInterfaceCompleteEvent
113
+ | AutoBeTestCompleteEvent
114
+ | AutoBeRealizeCompleteEvent,
115
+ ): Record<string, number> => {
116
+ switch (data.type) {
117
+ case "analyzeComplete": {
118
+ return {
119
+ Documents: Object.keys(data.files).length,
120
+ "Actor Roles": data.roles.length,
121
+ };
122
+ }
123
+ case "prismaComplete": {
124
+ return {
125
+ Namespaces: data.result.data.files.length,
126
+ Models: data.result.data.files
127
+ .map((f: { models: unknown[] }) => f.models.length)
128
+ .reduce((a: number, b: number) => a + b, 0),
129
+ };
130
+ }
131
+ case "interfaceComplete": {
132
+ return {
133
+ Operations: data.document.operations.length,
134
+ Schemas: Object.keys(data.document.components.schemas).length,
135
+ };
136
+ }
137
+ case "testComplete": {
138
+ return {
139
+ Functions: data.files.length,
140
+ };
141
+ }
142
+ case "realizeComplete": {
143
+ return {
144
+ Controllers: Object.keys(data.controllers).length,
145
+ Functions: data.functions.length,
146
+ };
147
+ }
148
+ default:
149
+ data satisfies never;
150
+ throw new Error(`Unknown step type: ${data}`);
151
+ }
152
+ };
@@ -0,0 +1,152 @@
1
+ import { IAutoBeTokenUsageJson } from "@autobe/interface";
2
+ import { ReactNode } from "react";
3
+
4
+ import { COLORS } from "../constant/color";
5
+ import { toCompactNumberFormat } from "../utils";
6
+
7
+ /** Props interface for TokenUsageCard component */
8
+ interface ITokenUsageCardProps {
9
+ /** Token usage data to display */
10
+ tokenUsage: IAutoBeTokenUsageJson | null;
11
+ }
12
+
13
+ /** Props interface for TokenRow component */
14
+ interface ITokenRowProps {
15
+ /** Label text */
16
+ label: string;
17
+ /** Value to display */
18
+ value: ReactNode;
19
+ /** Whether this is the total row with border */
20
+ isTotal?: boolean;
21
+ }
22
+
23
+ /** Props interface for TokenLabel component */
24
+ interface ITokenLabelProps {
25
+ /** Label text */
26
+ children: string;
27
+ /** Whether this is a total label */
28
+ isTotal?: boolean;
29
+ }
30
+
31
+ /** Props interface for TokenValue component */
32
+ interface ITokenValueProps {
33
+ /** Value content */
34
+ children: ReactNode;
35
+ /** Color variant for the value */
36
+ variant: "input" | "cachedInput" | "output" | "total";
37
+ }
38
+
39
+ /** Token row component with consistent flex layout */
40
+ const TokenRow = ({ label, value, isTotal = false }: ITokenRowProps) => (
41
+ <div
42
+ style={{
43
+ display: "flex",
44
+ justifyContent: "space-between",
45
+ alignItems: "center",
46
+ ...(isTotal && {
47
+ paddingTop: "8px",
48
+ borderTop: `1px solid ${COLORS.GRAY_BORDER_LIGHT}`,
49
+ }),
50
+ }}
51
+ >
52
+ <TokenLabel isTotal={isTotal}>{label}</TokenLabel>
53
+ {value}
54
+ </div>
55
+ );
56
+
57
+ /** Token label component with consistent styling */
58
+ const TokenLabel = ({ children, isTotal = false }: ITokenLabelProps) => (
59
+ <span
60
+ style={{
61
+ color: isTotal ? COLORS.GRAY_TEXT_DARK : COLORS.GRAY_TEXT_MEDIUM,
62
+ fontWeight: isTotal ? "600" : "500",
63
+ }}
64
+ >
65
+ {children}
66
+ </span>
67
+ );
68
+
69
+ /** Token value component with variant-based styling */
70
+ const TokenValue = ({ children, variant }: ITokenValueProps) => {
71
+ const getVariantStyles = () => {
72
+ switch (variant) {
73
+ case "input":
74
+ return { color: COLORS.TOKEN_INPUT, fontWeight: "600" };
75
+ case "cachedInput":
76
+ return {
77
+ color: COLORS.GRAY_TEXT_MEDIUM,
78
+ fontWeight: "400",
79
+ fontSize: "0.875rem",
80
+ };
81
+ case "output":
82
+ return { color: COLORS.TOKEN_OUTPUT, fontWeight: "600" };
83
+ case "total":
84
+ return {
85
+ color: COLORS.TOKEN_TOTAL,
86
+ fontWeight: "700",
87
+ fontSize: "1.125rem",
88
+ };
89
+ default:
90
+ variant satisfies never;
91
+ return {};
92
+ }
93
+ };
94
+
95
+ return <span style={getVariantStyles()}>{children}</span>;
96
+ };
97
+
98
+ /**
99
+ * Token usage card component displaying input, output, and total token counts
100
+ *
101
+ * @param props - Component props
102
+ * @returns JSX element representing the token usage card
103
+ */
104
+ export const AutoBeTokenUsage = ({ tokenUsage }: ITokenUsageCardProps) => {
105
+ return (
106
+ <>
107
+ <div
108
+ style={{
109
+ display: "flex",
110
+ flexDirection: "column",
111
+ gap: "8px",
112
+ }}
113
+ >
114
+ <TokenRow
115
+ label="Input:"
116
+ value={
117
+ <TokenValue variant="input">
118
+ {toCompactNumberFormat(tokenUsage?.aggregate.input.total ?? 0)}
119
+ </TokenValue>
120
+ }
121
+ />
122
+ <TokenRow
123
+ label="Cached Input:"
124
+ value={
125
+ <TokenValue variant="cachedInput">
126
+ {toCompactNumberFormat(tokenUsage?.aggregate.input.cached ?? 0)}
127
+ </TokenValue>
128
+ }
129
+ />
130
+ <TokenRow
131
+ label="Output:"
132
+ value={
133
+ <TokenValue variant="output">
134
+ {toCompactNumberFormat(tokenUsage?.aggregate.output.total ?? 0)}
135
+ </TokenValue>
136
+ }
137
+ />
138
+ <TokenRow
139
+ label="Total:"
140
+ value={
141
+ <TokenValue variant="total">
142
+ {toCompactNumberFormat(tokenUsage?.aggregate.total ?? 0)}
143
+ </TokenValue>
144
+ }
145
+ isTotal
146
+ />
147
+ </div>
148
+ </>
149
+ );
150
+ };
151
+
152
+ export default AutoBeTokenUsage;
@@ -0,0 +1,4 @@
1
+ export * from "./AutoBeChatBanner";
2
+ export * from "./AutoBeTokenUsage";
3
+ export * from "./AutoBeChatState";
4
+ export * from "./AutoBeAgentInformation";
@@ -0,0 +1,95 @@
1
+ import { ReactNode, useState } from "react";
2
+
3
+ import { COLORS } from "../constant/color";
4
+
5
+ /** Props interface for Collapsible component */
6
+ interface ICollapsibleProps {
7
+ /** Title to display in the header */
8
+ title: string;
9
+ /** Content to be collapsed/expanded */
10
+ children: ReactNode;
11
+ /** Initial collapsed state */
12
+ defaultCollapsed?: boolean;
13
+ /** Whether to show animation */
14
+ animated?: boolean;
15
+ }
16
+
17
+ /** Reusable collapsible component with toggle functionality */
18
+ export const Collapsible = ({
19
+ title,
20
+ children,
21
+ defaultCollapsed = false,
22
+ animated = true,
23
+ }: ICollapsibleProps) => {
24
+ const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
25
+
26
+ return (
27
+ <div>
28
+ <div
29
+ style={{
30
+ display: "flex",
31
+ alignItems: "center",
32
+ marginBottom: isCollapsed ? "0" : "12px",
33
+ }}
34
+ >
35
+ <button
36
+ onClick={() => setIsCollapsed(!isCollapsed)}
37
+ style={{
38
+ background: "none",
39
+ border: "none",
40
+ cursor: "pointer",
41
+ padding: "4px",
42
+ borderRadius: "4px",
43
+ display: "flex",
44
+ alignItems: "center",
45
+ justifyContent: "center",
46
+ transition: animated ? "background-color 0.2s" : "none",
47
+ color: COLORS.GRAY_TEXT_MEDIUM,
48
+ marginRight: "8px",
49
+ }}
50
+ onMouseEnter={(e) => {
51
+ e.currentTarget.style.backgroundColor = COLORS.GRAY_BORDER_LIGHT;
52
+ }}
53
+ onMouseLeave={(e) => {
54
+ e.currentTarget.style.backgroundColor = "transparent";
55
+ }}
56
+ >
57
+ <svg
58
+ width="16"
59
+ height="16"
60
+ viewBox="0 0 24 24"
61
+ fill="currentColor"
62
+ style={{
63
+ transform: isCollapsed ? "rotate(-90deg)" : "rotate(0deg)",
64
+ transition: animated ? "transform 0.2s ease" : "none",
65
+ }}
66
+ >
67
+ <path d="M7.41 8.58L12 13.17l4.59-4.59L18 10l-6 6-6-6 1.41-1.42z" />
68
+ </svg>
69
+ </button>
70
+ <h3
71
+ style={{
72
+ margin: 0,
73
+ fontSize: "1.125rem",
74
+ fontWeight: "600",
75
+ color: COLORS.GRAY_TEXT_DARK,
76
+ }}
77
+ >
78
+ {title}
79
+ </h3>
80
+ </div>
81
+ {!isCollapsed && (
82
+ <div
83
+ style={{
84
+ opacity: isCollapsed ? 0 : 1,
85
+ transition: animated ? "opacity 0.2s ease" : "none",
86
+ }}
87
+ >
88
+ {children}
89
+ </div>
90
+ )}
91
+ </div>
92
+ );
93
+ };
94
+
95
+ export default Collapsible;
@@ -0,0 +1,2 @@
1
+ export * from "./Collapsible";
2
+ export * from "./ChatBubble";