@canmingir/link 1.2.55 → 1.2.57

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 (220) hide show
  1. package/.eslintignore +0 -1
  2. package/.github/workflows/publish.yml +6 -1
  3. package/LICENSE +201 -0
  4. package/bun.lock +20 -30
  5. package/package.json +4 -1
  6. package/src/assets/illustrations/avatar-shape.jsx +10 -8
  7. package/src/assets/illustrations/background-shape.jsx +0 -2
  8. package/src/assets/illustrations/booking-illustration.jsx +8 -6
  9. package/src/assets/illustrations/check-in-illustration.jsx +8 -6
  10. package/src/assets/illustrations/check-out-illustration.jsx +8 -6
  11. package/src/assets/illustrations/coming-soon-illustration.jsx +8 -6
  12. package/src/assets/illustrations/forbidden-illustration.jsx +8 -6
  13. package/src/assets/illustrations/maintenance-illustration.jsx +8 -6
  14. package/src/assets/illustrations/motivation-illustration.jsx +8 -6
  15. package/src/assets/illustrations/order-complete-illustration.jsx +8 -6
  16. package/src/assets/illustrations/page-not-found-illustration.jsx +8 -6
  17. package/src/assets/illustrations/seo-illustration.jsx +8 -6
  18. package/src/assets/illustrations/sever-error-illustration.jsx +8 -6
  19. package/src/assets/illustrations/upgrade-storage-illustration.jsx +8 -6
  20. package/src/assets/illustrations/upload-illustration.jsx +8 -6
  21. package/src/components/Iconify/Iconify.jsx +0 -1
  22. package/src/components/chart/chart.js +0 -2
  23. package/src/components/chart/index.js +0 -2
  24. package/src/components/chart/use-chart.js +0 -2
  25. package/src/components/custom-popover/styles.js +0 -2
  26. package/src/components/custom-popover/use-popover.js +0 -2
  27. package/src/components/custom-popover/utils.js +0 -2
  28. package/src/components/file-thumbnail/utils.js +0 -12
  29. package/src/components/image/utils.js +0 -2
  30. package/src/components/label/styles.js +0 -2
  31. package/src/components/logo/logo.jsx +1 -1
  32. package/src/components/scrollbar/styles.js +0 -2
  33. package/src/components/settings/context/settings-context.js +0 -2
  34. package/src/hooks/use-boolean.js +0 -2
  35. package/src/hooks/use-event-listener.js +0 -2
  36. package/src/hooks/use-local-storage.js +0 -4
  37. package/src/hooks/use-off-set-top.js +0 -2
  38. package/src/hooks/use-responsive.js +0 -4
  39. package/src/hooks/use-scroll-to-top.js +0 -2
  40. package/src/layouts/CompactLayout/CompactLayout.jsx +0 -2
  41. package/src/layouts/DashboardLayout/DashboardLayout.jsx +0 -2
  42. package/src/layouts/DashboardLayout/main.jsx +0 -2
  43. package/src/layouts/DashboardLayout/nav-horizontal.jsx +0 -2
  44. package/src/layouts/DashboardLayout/nav-vertical.jsx +6 -5
  45. package/src/layouts/FullScreenLayout/FullScreenLayout.jsx +3 -4
  46. package/src/layouts/FullScreenLayout/nav-horizontal.jsx +0 -2
  47. package/src/layouts/FullScreenLayout/nav-vertical.jsx +4 -5
  48. package/src/layouts/MainLayout/header.jsx +1 -3
  49. package/src/layouts/MainLayout/nav/desktop/index.jsx +0 -2
  50. package/src/layouts/MainLayout/nav/desktop/nav-item.jsx +0 -6
  51. package/src/layouts/MainLayout/nav/desktop/nav-list.jsx +16 -16
  52. package/src/layouts/MainLayout/nav/mobile/index.jsx +1 -3
  53. package/src/layouts/MainLayout/nav/mobile/nav-item.jsx +0 -4
  54. package/src/layouts/MainLayout/nav/mobile/nav-list.jsx +0 -2
  55. package/src/layouts/TwoSideLayout/TwoSideLayout.jsx +0 -1
  56. package/src/layouts/auth/classic.jsx +3 -4
  57. package/src/layouts/auth/modern-compact.jsx +0 -2
  58. package/src/layouts/auth/modern.jsx +0 -2
  59. package/src/layouts/common/ProjectBar/utils.js +0 -10
  60. package/src/layouts/common/account-popover.jsx +0 -2
  61. package/src/layouts/common/header-shadow.jsx +0 -2
  62. package/src/layouts/common/header-sim.jsx +7 -4
  63. package/src/layouts/common/header-simple.jsx +14 -8
  64. package/src/layouts/common/nav-toggle-button.jsx +1 -3
  65. package/src/layouts/common/notifications-popover/index.jsx +9 -10
  66. package/src/layouts/common/notifications-popover/notification-item.jsx +20 -17
  67. package/src/layouts/common/settings-button.jsx +0 -2
  68. package/src/layouts/config-layout.js +0 -2
  69. package/src/lib/ChatMessage/AIMessage.tsx +163 -0
  70. package/src/lib/ChatMessage/ErrorMessage.tsx +63 -0
  71. package/src/lib/ChatMessage/HumanMessage.tsx +142 -0
  72. package/src/lib/ChatMessage/LoadingMessage.tsx +27 -0
  73. package/src/lib/ChatMessage/MessageList.tsx +81 -0
  74. package/src/lib/ChatMessage/ToolMessage.tsx +346 -0
  75. package/src/lib/ChatMessage/index.js +6 -0
  76. package/src/lib/Image/utils.js +0 -2
  77. package/src/lib/PresetSelector/PresetSelector.tsx +112 -0
  78. package/src/lib/PresetSelector/index.js +1 -0
  79. package/src/lib/Scrollbar/styles.js +0 -2
  80. package/src/lib/SidebarChat/ChatDrawer.tsx +407 -0
  81. package/src/lib/SidebarChat/SessionPopover.tsx +221 -0
  82. package/src/lib/SidebarChat/SidebarChat.tsx +222 -0
  83. package/src/lib/SidebarChat/SidebarSessionList.tsx +304 -0
  84. package/src/lib/SidebarChat/assets.d.ts +4 -0
  85. package/src/lib/SidebarChat/cleanIconName.ts +4 -0
  86. package/src/lib/SidebarChat/index.js +1 -0
  87. package/src/lib/SidebarChat/messageSFX.mp3 +0 -0
  88. package/src/lib/SidebarChat/types.ts +14 -0
  89. package/src/lib/index.js +11 -0
  90. package/src/pages/404.jsx +0 -2
  91. package/src/routes/components/router-link.jsx +0 -2
  92. package/src/routes/hooks/use-active-link.js +0 -2
  93. package/src/routes/hooks/use-pathname.js +0 -2
  94. package/src/routes/hooks/use-router.js +0 -2
  95. package/src/theme/css.js +0 -12
  96. package/src/theme/custom-shadows.js +0 -2
  97. package/src/theme/options/contrast.js +0 -2
  98. package/src/theme/options/presets.js +0 -4
  99. package/src/theme/options/right-to-left.jsx +0 -2
  100. package/src/theme/overrides/components/accordion.js +0 -2
  101. package/src/theme/overrides/components/alert.js +0 -4
  102. package/src/theme/overrides/components/appbar.js +0 -2
  103. package/src/theme/overrides/components/autocomplete.js +0 -2
  104. package/src/theme/overrides/components/avatar.js +0 -4
  105. package/src/theme/overrides/components/backdrop.js +0 -2
  106. package/src/theme/overrides/components/badge.js +0 -2
  107. package/src/theme/overrides/components/breadcrumbs.js +0 -2
  108. package/src/theme/overrides/components/button-group.js +0 -4
  109. package/src/theme/overrides/components/button.js +0 -4
  110. package/src/theme/overrides/components/card.js +0 -2
  111. package/src/theme/overrides/components/checkbox.js +0 -2
  112. package/src/theme/overrides/components/chip.js +0 -4
  113. package/src/theme/overrides/components/css-baseline.js +0 -2
  114. package/src/theme/overrides/components/data-grid.js +0 -2
  115. package/src/theme/overrides/components/date-picker.jsx +0 -2
  116. package/src/theme/overrides/components/dialog.js +0 -2
  117. package/src/theme/overrides/components/drawer.js +0 -2
  118. package/src/theme/overrides/components/fab.js +0 -4
  119. package/src/theme/overrides/components/form.js +0 -2
  120. package/src/theme/overrides/components/list.js +0 -2
  121. package/src/theme/overrides/components/loading-button.js +0 -2
  122. package/src/theme/overrides/components/menu.js +0 -2
  123. package/src/theme/overrides/components/pagination.js +0 -4
  124. package/src/theme/overrides/components/paper.js +0 -2
  125. package/src/theme/overrides/components/popover.js +0 -2
  126. package/src/theme/overrides/components/progress.js +0 -4
  127. package/src/theme/overrides/components/radio.js +0 -2
  128. package/src/theme/overrides/components/rating.js +0 -2
  129. package/src/theme/overrides/components/select.js +0 -2
  130. package/src/theme/overrides/components/skeleton.js +0 -2
  131. package/src/theme/overrides/components/slider.js +0 -2
  132. package/src/theme/overrides/components/stepper.js +0 -2
  133. package/src/theme/overrides/components/svg-icon.js +0 -2
  134. package/src/theme/overrides/components/switch.js +0 -2
  135. package/src/theme/overrides/components/table.js +0 -2
  136. package/src/theme/overrides/components/tabs.js +0 -2
  137. package/src/theme/overrides/components/textfield.js +0 -2
  138. package/src/theme/overrides/components/timeline.js +0 -2
  139. package/src/theme/overrides/components/toggle-button.js +0 -4
  140. package/src/theme/overrides/components/tooltip.js +0 -2
  141. package/src/theme/overrides/components/tree-view.js +0 -2
  142. package/src/theme/overrides/components/typography.js +0 -2
  143. package/src/theme/overrides/default-props.jsx +4 -7
  144. package/src/theme/palette.js +0 -4
  145. package/src/theme/shadows.js +0 -2
  146. package/src/theme/typography.js +0 -4
  147. package/src/utils/flatten-array.js +0 -2
  148. package/src/utils/format-number.js +0 -2
  149. package/src/utils/format-time.js +0 -2
  150. package/src/utils/storage-available.js +0 -2
  151. package/src/widgets/Login/Login.jsx +0 -2
  152. package/src/widgets/error/not-found-view.jsx +0 -2
  153. package/vite/vite.js +8 -1
  154. package/project/.eslintrc.cjs +0 -22
  155. package/project/config.js +0 -18
  156. package/project/config.menu.js +0 -39
  157. package/project/config.template.js +0 -31
  158. package/project/cypress/e2e/layouts.cy.js +0 -170
  159. package/project/cypress/e2e/login.cy.js +0 -35
  160. package/project/cypress/e2e/selectbar.cy.js +0 -80
  161. package/project/cypress/fixtures/CONFIG/MENU_CONFIG.js +0 -39
  162. package/project/cypress/fixtures/EMPEROR/EMPERORS_GET.json +0 -51
  163. package/project/cypress/fixtures/EMPEROR/EMPERORS_POST.json +0 -9
  164. package/project/cypress/fixtures/OAUTH/GITHUB/user.json +0 -46
  165. package/project/cypress/fixtures/example.json +0 -5
  166. package/project/cypress/support/commands.js +0 -104
  167. package/project/cypress/support/e2e.js +0 -1
  168. package/project/cypress.config.js +0 -10
  169. package/project/index.html +0 -12
  170. package/project/media/ProjectIcons/3d.png +0 -0
  171. package/project/media/ProjectIcons/box.png +0 -0
  172. package/project/media/ProjectIcons/cafe.png +0 -0
  173. package/project/media/ProjectIcons/cargo.png +0 -0
  174. package/project/media/ProjectIcons/cloud.png +0 -0
  175. package/project/media/ProjectIcons/code.png +0 -0
  176. package/project/media/ProjectIcons/company.png +0 -0
  177. package/project/media/ProjectIcons/conversation.png +0 -0
  178. package/project/media/ProjectIcons/distributed.png +0 -0
  179. package/project/media/ProjectIcons/fingerprint.png +0 -0
  180. package/project/media/ProjectIcons/forest.png +0 -0
  181. package/project/media/ProjectIcons/headphones.png +0 -0
  182. package/project/media/ProjectIcons/office.png +0 -0
  183. package/project/media/ProjectIcons/path.png +0 -0
  184. package/project/media/ProjectIcons/printer.png +0 -0
  185. package/project/media/ProjectIcons/project.png +0 -0
  186. package/project/media/ProjectIcons/resume.png +0 -0
  187. package/project/media/ProjectIcons/rocket.png +0 -0
  188. package/project/media/ProjectIcons/rtruck.png +0 -0
  189. package/project/media/ProjectIcons/sign.png +0 -0
  190. package/project/media/ProjectIcons/site.png +0 -0
  191. package/project/media/ProjectIcons/truck.png +0 -0
  192. package/project/media/logo.png +0 -0
  193. package/project/package-lock.json +0 -22434
  194. package/project/package.json +0 -42
  195. package/project/public/assets/background/overlay_2.jpg +0 -0
  196. package/project/public/assets/illustrations/illustration_dashboard.png +0 -0
  197. package/project/public/media/largeLogo.png +0 -0
  198. package/project/public/media/logo.png +0 -0
  199. package/project/public/vite.svg +0 -1
  200. package/project/routes.jsx +0 -47
  201. package/project/server/mock.json +0 -110
  202. package/project/server/oauthMock.js +0 -27
  203. package/project/server/package-lock.json +0 -1505
  204. package/project/server/package.json +0 -20
  205. package/project/server/server.js +0 -127
  206. package/project/src/Container.jsx +0 -7
  207. package/project/src/components/ActionButton.jsx +0 -18
  208. package/project/src/hooks/useEmperor.jsx +0 -21
  209. package/project/src/http/index.js +0 -39
  210. package/project/src/main.jsx +0 -10
  211. package/project/src/pages/Battles.jsx +0 -16
  212. package/project/src/pages/Emperor.jsx +0 -50
  213. package/project/src/pages/index.jsx +0 -33
  214. package/project/src/theme.js +0 -151
  215. package/project/src/widgets/ActionButton.jsx +0 -68
  216. package/project/src/widgets/AddNewEmperor.jsx +0 -120
  217. package/project/src/widgets/ProjectContainer.jsx +0 -27
  218. package/project/vite.config.js +0 -6
  219. package/project/vite.config.js.timestamp-1709564450418-45fdd27900806.mjs +0 -341
  220. package/src/layouts/MainLayout/footer.jsx +0 -153
@@ -0,0 +1,221 @@
1
+ import { Iconify } from "@canmingir/link/platform/components";
2
+ import { MessageList } from "../ChatMessage";
3
+ import { Scrollbar } from "@canmingir/link/platform/components";
4
+ import Stack from "@mui/material/Stack";
5
+ import { StoredSession } from "./types";
6
+ import { alpha } from "@mui/material/styles";
7
+ import { cleanIconName } from "./cleanIconName";
8
+
9
+ import { Box, IconButton, Popover, Tooltip, Typography } from "@mui/material";
10
+ import React, { memo, useEffect, useRef } from "react";
11
+
12
+ interface SessionPopoverProps {
13
+ anchorEl: HTMLElement | null;
14
+ onClose: () => void;
15
+ onOpenFullChat: () => void;
16
+ sessions: StoredSession[];
17
+ activeSessionId: string;
18
+ currentSessionId?: string;
19
+ selectedConversationId?: string;
20
+ messagesEndRef: React.RefObject<HTMLDivElement>;
21
+ highlightedMessage: React.RefObject<HTMLDivElement>;
22
+ }
23
+
24
+ const SessionPopover = ({
25
+ anchorEl,
26
+ onClose,
27
+ onOpenFullChat,
28
+ sessions,
29
+ activeSessionId,
30
+ currentSessionId,
31
+ selectedConversationId,
32
+ messagesEndRef,
33
+ highlightedMessage,
34
+ }: SessionPopoverProps) => {
35
+ const session = sessions.find((s) => s.sessionId === activeSessionId);
36
+ const isCurrent = activeSessionId === currentSessionId;
37
+ const popoverEndRef = useRef<HTMLDivElement>(null);
38
+
39
+ useEffect(() => {
40
+ if (anchorEl) {
41
+ setTimeout(() => {
42
+ popoverEndRef.current?.scrollIntoView({ behavior: "smooth" });
43
+ }, 80);
44
+ }
45
+ }, [anchorEl, activeSessionId]);
46
+
47
+ return (
48
+ <Popover
49
+ open={Boolean(anchorEl)}
50
+ anchorEl={anchorEl}
51
+ onClose={onClose}
52
+ anchorOrigin={{ vertical: "center", horizontal: "left" }}
53
+ transformOrigin={{ vertical: "center", horizontal: "right" }}
54
+ slotProps={{
55
+ paper: {
56
+ sx: {
57
+ width: 360,
58
+ height: 500,
59
+ display: "flex",
60
+ flexDirection: "column",
61
+ overflow: "hidden",
62
+ borderRadius: "16px",
63
+ bgcolor: (theme) =>
64
+ alpha(
65
+ theme.palette.mode === "dark"
66
+ ? theme.palette.grey[900]
67
+ : theme.palette.common.white,
68
+ 0.88
69
+ ),
70
+ backdropFilter: "blur(20px) saturate(180%)",
71
+ WebkitBackdropFilter: "blur(20px) saturate(180%)",
72
+ border: (theme) =>
73
+ `1px solid ${alpha(theme.palette.divider, 0.12)}`,
74
+ boxShadow: (theme) =>
75
+ `0 8px 40px ${alpha(theme.palette.common.black, 0.22)}`,
76
+ mr: 1,
77
+ },
78
+ },
79
+ }}
80
+ >
81
+ <Box
82
+ sx={{
83
+ flexShrink: 0,
84
+ px: 2,
85
+ py: 1.5,
86
+ display: "flex",
87
+ alignItems: "center",
88
+ justifyContent: "space-between",
89
+ background: (theme) =>
90
+ `linear-gradient(135deg, ${alpha(
91
+ theme.palette.grey[900],
92
+ 0.97
93
+ )} 0%, ${alpha(theme.palette.grey[800], 0.97)} 100%)`,
94
+ borderBottom: (theme) =>
95
+ `1px solid ${alpha(theme.palette.common.white, 0.06)}`,
96
+ }}
97
+ >
98
+ <Stack direction="row" alignItems="center" spacing={1}>
99
+ <Box
100
+ sx={{
101
+ width: 32,
102
+ height: 32,
103
+ borderRadius: "9px",
104
+ bgcolor: (theme) => (theme.palette.primary.main, 0.2),
105
+ border: (theme) =>
106
+ `1px solid ${alpha(theme.palette.primary.main, 0.3)}`,
107
+ display: "flex",
108
+ alignItems: "center",
109
+ justifyContent: "center",
110
+ flexShrink: 0,
111
+ }}
112
+ >
113
+ {session?.agentIcon ? (
114
+ <Iconify
115
+ icon={cleanIconName(session.agentIcon)}
116
+ sx={{ width: 18, height: 18 }}
117
+ />
118
+ ) : (
119
+ <Typography
120
+ sx={{
121
+ fontSize: "0.72rem",
122
+ fontWeight: 800,
123
+ color: (theme) =>
124
+ isCurrent
125
+ ? theme.palette.primary.light
126
+ : theme.palette.warning.light,
127
+ }}
128
+ >
129
+ {session?.agentName?.[0] ?? "?"}
130
+ </Typography>
131
+ )}
132
+ </Box>
133
+ <Box>
134
+ <Typography
135
+ variant="subtitle2"
136
+ sx={{ color: "white", fontWeight: 700, lineHeight: 1.2 }}
137
+ >
138
+ {session?.agentName}
139
+ </Typography>
140
+ <Typography
141
+ variant="caption"
142
+ sx={{
143
+ color: (theme) => alpha(theme.palette.common.white, 0.5),
144
+ lineHeight: 1,
145
+ }}
146
+ >
147
+ {isCurrent ? "Current · " : "Session · "}
148
+ {session?.messages.length ?? 0} msg
149
+ {session ? ` · ${session.agentName}` : ""}
150
+ </Typography>
151
+ </Box>
152
+ </Stack>
153
+
154
+ <Stack direction="row" spacing={0.5}>
155
+ <Tooltip title="Open full chat" placement="top">
156
+ <IconButton
157
+ size="small"
158
+ onClick={onOpenFullChat}
159
+ sx={{
160
+ color: (theme) => alpha(theme.palette.common.white, 0.7),
161
+ "&:hover": { bgcolor: alpha("#fff", 0.08), color: "white" },
162
+ }}
163
+ >
164
+ <Iconify
165
+ icon="solar:maximize-square-bold"
166
+ sx={{ width: 16, height: 16 }}
167
+ />
168
+ </IconButton>
169
+ </Tooltip>
170
+ <IconButton
171
+ size="small"
172
+ onClick={onClose}
173
+ sx={{
174
+ color: (theme) => alpha(theme.palette.common.white, 0.7),
175
+ "&:hover": { bgcolor: alpha("#fff", 0.08), color: "white" },
176
+ }}
177
+ >
178
+ <Iconify icon="mdi:close" sx={{ width: 16, height: 16 }} />
179
+ </IconButton>
180
+ </Stack>
181
+ </Box>
182
+
183
+ <Box sx={{ flex: 1, minHeight: 0, overflow: "hidden" }}>
184
+ {session ? (
185
+ <Scrollbar sx={{ height: "100%", px: 1.5, py: 1 }}>
186
+ <MessageList
187
+ messages={
188
+ session.messages as {
189
+ id: string;
190
+ content: string;
191
+ role: string;
192
+ }[]
193
+ }
194
+ selectedId={isCurrent ? selectedConversationId : undefined}
195
+ messagesEndRef={messagesEndRef}
196
+ highlightedMessage={highlightedMessage}
197
+ />
198
+ <div ref={popoverEndRef} />
199
+ </Scrollbar>
200
+ ) : (
201
+ <Box
202
+ sx={{
203
+ height: "100%",
204
+ display: "flex",
205
+ flexDirection: "column",
206
+ alignItems: "center",
207
+ justifyContent: "center",
208
+ gap: 1,
209
+ opacity: 0.4,
210
+ }}
211
+ >
212
+ <Iconify icon="solar:history-bold" sx={{ width: 32, height: 32 }} />
213
+ <Typography variant="caption">No messages yet</Typography>
214
+ </Box>
215
+ )}
216
+ </Box>
217
+ </Popover>
218
+ );
219
+ };
220
+
221
+ export default memo(SessionPopover);
@@ -0,0 +1,222 @@
1
+ import ChatDrawer from "./ChatDrawer";
2
+ import SessionPopover from "./SessionPopover";
3
+ import SidebarSessionList from "./SidebarSessionList";
4
+ import { StoredSession } from "./types";
5
+ import { ToolDecision, ToolRenderers } from "../ChatMessage/ToolMessage";
6
+ import { useEvent } from "@nucleoidai/react-event";
7
+ import useSound from "use-sound";
8
+
9
+ import MessageSfx from "./messageSFX.mp3";
10
+ import React, { memo, useCallback, useEffect, useRef, useState } from "react";
11
+
12
+ type Message = { id?: string; content: string; role: string };
13
+
14
+ interface SidebarChatProps {
15
+ selectedConversationId?: string;
16
+ sessionId?: string;
17
+ title: string;
18
+ open: boolean;
19
+ handleToggle: () => void;
20
+ handleNewUserMessage: (message: string) => void;
21
+ history?: Message[];
22
+ readOnly?: boolean;
23
+ sound?: boolean;
24
+ agent?: { id: string; name: string; icon: string };
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ Presets?: any[];
27
+ selectedPreset?: string;
28
+ onPresetChange?: (preset: string) => void;
29
+ onSessionSelect?: (sessionId: string, cachedMessages?: Message[]) => void;
30
+ onNewSession?: () => void;
31
+ beta?: boolean;
32
+ storedSessions?: StoredSession[];
33
+ refreshSessions?: () => void;
34
+ clearAllSessions?: () => void;
35
+ toolRenderers?: ToolRenderers;
36
+ onToolDecision?: (toolCallId: string, decision: ToolDecision) => void;
37
+ }
38
+
39
+ const SidebarChat = ({
40
+ selectedConversationId,
41
+ sessionId,
42
+ title,
43
+ open,
44
+ handleToggle,
45
+ handleNewUserMessage,
46
+ history = [],
47
+ readOnly,
48
+ sound,
49
+ agent,
50
+ Presets = [],
51
+ selectedPreset,
52
+ onPresetChange,
53
+ onSessionSelect,
54
+ onNewSession,
55
+ beta,
56
+ storedSessions = [],
57
+ refreshSessions = () => {},
58
+ clearAllSessions = () => {},
59
+ toolRenderers,
60
+ onToolDecision,
61
+ }: SidebarChatProps) => {
62
+ const [aiResponded] = useEvent("AI_RESPONDED", null);
63
+ const [conversationSent] = useEvent("CONVERSATION_SENT", null);
64
+
65
+ const currentSessionId = sessionId || selectedConversationId;
66
+
67
+ const [mute, setMute] = useState(false);
68
+ const [loading, setLoading] = useState(false);
69
+ const [unreadCount, setUnreadCount] = useState(0);
70
+ const [play] = useSound(MessageSfx);
71
+
72
+ const messagesEndRef = useRef(null);
73
+ const highlightedMessage = useRef(null);
74
+ const lastSeenCountRef = useRef(0);
75
+ const sidebarRef = useRef<HTMLDivElement>(null);
76
+
77
+ const [popoverAnchor, setPopoverAnchor] = useState<HTMLElement | null>(null);
78
+ const [activeLogSessionId, setActiveLogSessionId] = useState("");
79
+
80
+ const scrollToBottom = useCallback(() => {
81
+ highlightedMessage.current?.scrollIntoView({
82
+ behavior: "smooth",
83
+ block: "center",
84
+ });
85
+ }, []);
86
+
87
+ useEffect(() => {
88
+ if (open) {
89
+ lastSeenCountRef.current = history.length;
90
+ setUnreadCount(0);
91
+ setPopoverAnchor(null);
92
+ }
93
+ }, [open, history.length]);
94
+
95
+ useEffect(() => {
96
+ if (!open && history.length > lastSeenCountRef.current) {
97
+ setUnreadCount(history.length - lastSeenCountRef.current);
98
+ }
99
+ }, [history, open]);
100
+
101
+ useEffect(() => {
102
+ if (open) {
103
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
104
+ }
105
+ }, [history, open]);
106
+
107
+ useEffect(() => {
108
+ scrollToBottom();
109
+ }, [aiResponded, scrollToBottom]);
110
+
111
+ useEffect(() => {
112
+ if (selectedConversationId) scrollToBottom();
113
+ }, [selectedConversationId, scrollToBottom]);
114
+
115
+ useEffect(() => {
116
+ if (!sound) {
117
+ setLoading(false);
118
+ return;
119
+ } else if (aiResponded !== null) {
120
+ setLoading(false);
121
+ !mute && play();
122
+ }
123
+ // eslint-disable-next-line
124
+ }, [aiResponded, mute, play]);
125
+
126
+ const handleSend = useCallback(
127
+ (content: string) => {
128
+ if (!content.trim()) return;
129
+ handleNewUserMessage(content);
130
+ !mute && play();
131
+ setLoading(true);
132
+ },
133
+ [handleNewUserMessage, mute, play]
134
+ );
135
+
136
+ const showLoading = useCallback(() => {
137
+ if (conversationSent?.createdAt > aiResponded?.createdAt) return true;
138
+ if (loading) return true;
139
+ if (conversationSent && aiResponded === null) return true;
140
+ return false;
141
+ }, [conversationSent, aiResponded, loading]);
142
+
143
+ const handleSessionClick = useCallback(
144
+ (event: React.MouseEvent<HTMLElement>, sid: string) => {
145
+ refreshSessions();
146
+ setActiveLogSessionId(sid);
147
+ setPopoverAnchor(event.currentTarget);
148
+ },
149
+ [refreshSessions]
150
+ );
151
+
152
+ const handleOpenFromPopover = useCallback(() => {
153
+ setPopoverAnchor(null);
154
+ if (activeLogSessionId && activeLogSessionId !== currentSessionId) {
155
+ const cached = storedSessions.find(
156
+ (s) => s.sessionId === activeLogSessionId
157
+ );
158
+ onSessionSelect?.(activeLogSessionId, cached?.messages);
159
+ }
160
+ if (!open) handleToggle();
161
+ }, [
162
+ handleToggle,
163
+ open,
164
+ activeLogSessionId,
165
+ currentSessionId,
166
+ onSessionSelect,
167
+ storedSessions,
168
+ ]);
169
+
170
+ return (
171
+ <>
172
+ {!open && (
173
+ <SidebarSessionList
174
+ sessions={storedSessions}
175
+ currentSessionId={currentSessionId}
176
+ unreadCount={unreadCount}
177
+ onToggleChat={handleToggle}
178
+ onSessionClick={handleSessionClick}
179
+ onClearAll={clearAllSessions}
180
+ wrapperRef={sidebarRef}
181
+ beta={beta}
182
+ onNewSession={onNewSession}
183
+ />
184
+ )}
185
+
186
+ <SessionPopover
187
+ anchorEl={popoverAnchor}
188
+ onClose={() => setPopoverAnchor(null)}
189
+ onOpenFullChat={handleOpenFromPopover}
190
+ sessions={storedSessions}
191
+ activeSessionId={activeLogSessionId}
192
+ currentSessionId={currentSessionId}
193
+ selectedConversationId={selectedConversationId}
194
+ messagesEndRef={messagesEndRef}
195
+ highlightedMessage={highlightedMessage}
196
+ />
197
+
198
+ <ChatDrawer
199
+ title={title}
200
+ open={open}
201
+ onClose={handleToggle}
202
+ history={history}
203
+ selectedConversationId={selectedConversationId}
204
+ readOnly={readOnly}
205
+ mute={mute}
206
+ onMuteToggle={() => setMute((prev) => !prev)}
207
+ showLoading={showLoading()}
208
+ onSend={handleSend}
209
+ Presets={Presets}
210
+ selectedPreset={selectedPreset}
211
+ onPresetChange={onPresetChange}
212
+ messagesEndRef={messagesEndRef}
213
+ highlightedMessage={highlightedMessage}
214
+ onNewSession={onNewSession}
215
+ toolRenderers={toolRenderers}
216
+ onToolDecision={onToolDecision}
217
+ />
218
+ </>
219
+ );
220
+ };
221
+
222
+ export default memo(SidebarChat);