@canmingir/link 1.2.65 → 1.2.67

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.
@@ -0,0 +1,116 @@
1
+ import React from "react";
2
+ import { Box, Divider } from "@mui/material";
3
+
4
+ const DevToolFrame = ({
5
+ width = 62,
6
+ height = "auto",
7
+ top = "50%",
8
+ open = true,
9
+ content,
10
+ header,
11
+ footer,
12
+ sx,
13
+ }) => {
14
+ if (open === false) return null;
15
+
16
+ return (
17
+ <Box
18
+ component="aside"
19
+ sx={{
20
+ position: "fixed",
21
+ top,
22
+ right: 0,
23
+ transform: "translateY(-50%)",
24
+ width,
25
+ height,
26
+ bgcolor: (theme) =>
27
+ theme.palette.mode === "dark"
28
+ ? "background.paper"
29
+ : "rgba(255, 255, 255, 0.85)",
30
+ backdropFilter: "blur(16px) saturate(180%)",
31
+ WebkitBackdropFilter: "blur(16px) saturate(180%)",
32
+ border: (theme) =>
33
+ `1px solid ${
34
+ theme.palette.mode === "dark"
35
+ ? "rgba(255,255,255,0.07)"
36
+ : "rgba(0,0,0,0.07)"
37
+ }`,
38
+ borderRadius: "4px",
39
+ boxShadow: (theme) =>
40
+ theme.palette.mode === "dark"
41
+ ? "0 8px 32px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.04)"
42
+ : "0 8px 32px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04)",
43
+ zIndex: (theme) => theme.zIndex.modal + 3,
44
+ display: "flex",
45
+ flexDirection: "column",
46
+ alignItems: "center",
47
+ py: 1.5,
48
+ overflowY: "hidden",
49
+ overflowX: "hidden",
50
+ "&::-webkit-scrollbar": { display: "none" },
51
+ scrollbarWidth: "none",
52
+ ...(sx || {}),
53
+ }}
54
+ >
55
+ {header && (
56
+ <Box
57
+ sx={{
58
+ width: "100%",
59
+ display: "flex",
60
+ flexDirection: "column",
61
+ alignItems: "center",
62
+ flexShrink: 0,
63
+ }}
64
+ >
65
+ {header}
66
+ </Box>
67
+ )}
68
+
69
+ <Box
70
+ sx={{
71
+ flex: 1,
72
+ width: "100%",
73
+ display: "flex",
74
+ flexDirection: "column",
75
+ alignItems: "center",
76
+ overflowY: "auto",
77
+ overflowX: "hidden",
78
+ "&::-webkit-scrollbar": { display: "none" },
79
+ scrollbarWidth: "none",
80
+ }}
81
+ >
82
+ {content}
83
+ </Box>
84
+
85
+ {footer && (
86
+ <>
87
+ <Divider
88
+ flexItem
89
+ sx={{
90
+ borderColor: (theme) =>
91
+ theme.palette.mode === "dark"
92
+ ? "rgba(255,255,255,0.07)"
93
+ : "rgba(0,0,0,0.07)",
94
+ width: "100%",
95
+ flexShrink: 0,
96
+ }}
97
+ />
98
+ <Box
99
+ sx={{
100
+ width: "100%",
101
+ display: "flex",
102
+ flexDirection: "column",
103
+ alignItems: "center",
104
+ flexShrink: 0,
105
+ pt: 0.5,
106
+ }}
107
+ >
108
+ {footer}
109
+ </Box>
110
+ </>
111
+ )}
112
+ </Box>
113
+ );
114
+ };
115
+
116
+ export default DevToolFrame;
@@ -1,8 +1,8 @@
1
+ import { Iconify } from "@canmingir/link/platform/components";
2
+
1
3
  import React, { useEffect, useState } from "react";
2
4
  import { alpha, styled } from "@mui/material/styles";
3
5
 
4
- import { Iconify } from "@canmingir/link/platform/components";
5
-
6
6
  const ANIMATION_DELAY_MS = 200;
7
7
 
8
8
  const MainContainer = styled("div", {
@@ -79,6 +79,10 @@ const IconContainer = styled("div", {
79
79
  display: "flex",
80
80
  alignItems: "center",
81
81
  justifyContent: "center",
82
+ boxShadow: `
83
+ 4px 4px 0 rgba(255, 255, 255, 0.1),
84
+ 5px 5px 5px rgba(0, 0, 0, 0.5)
85
+ `,
82
86
 
83
87
  '[data-hovered="true"] &': {
84
88
  backgroundColor: hoverBg,
@@ -428,6 +428,7 @@ const ChatDrawer = ({
428
428
  onClose={onClose}
429
429
  variant="persistent"
430
430
  sx={{
431
+ zIndex: (theme) => theme.zIndex.modal + 3,
431
432
  width: open ? DRAWER_WIDTH : 0,
432
433
  flexShrink: 0,
433
434
  "& .MuiDrawer-paper": {
@@ -51,6 +51,7 @@ const SessionPopover = ({
51
51
  onClose={onClose}
52
52
  anchorOrigin={{ vertical: "center", horizontal: "left" }}
53
53
  transformOrigin={{ vertical: "center", horizontal: "right" }}
54
+ sx={{ zIndex: (theme) => theme.zIndex.modal + 3 }}
54
55
  slotProps={{
55
56
  paper: {
56
57
  sx: {
@@ -1,8 +1,5 @@
1
1
  import ChatDrawer from "./ChatDrawer";
2
2
  import MessageSfx from "./messageSFX.mp3";
3
- import SessionPopover from "./SessionPopover";
4
- import SidebarSessionList from "./SidebarSessionList";
5
- import { StoredSession } from "./types";
6
3
  import { useEvent } from "@nucleoidai/react-event";
7
4
  import useSound from "use-sound";
8
5
 
@@ -21,17 +18,11 @@ interface SidebarChatProps {
21
18
  history?: Message[];
22
19
  readOnly?: boolean;
23
20
  sound?: boolean;
24
- agent?: { id: string; name: string; icon: string };
25
21
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
22
  Presets?: any[];
27
23
  selectedPreset?: string;
28
24
  onPresetChange?: (preset: string) => void;
29
- onSessionSelect?: (sessionId: string, cachedMessages?: Message[]) => void;
30
25
  onNewSession?: () => void;
31
- beta?: boolean;
32
- storedSessions?: StoredSession[];
33
- refreshSessions?: () => void;
34
- clearAllSessions?: () => void;
35
26
  toolRenderers?: ToolRenderers;
36
27
  onToolDecision?: (toolCallId: string, decision: ToolDecision) => void;
37
28
  embedded?: boolean;
@@ -40,7 +31,6 @@ interface SidebarChatProps {
40
31
 
41
32
  const SidebarChat = ({
42
33
  selectedConversationId,
43
- sessionId,
44
34
  title,
45
35
  open,
46
36
  handleToggle,
@@ -48,16 +38,10 @@ const SidebarChat = ({
48
38
  history = [],
49
39
  readOnly,
50
40
  sound,
51
- agent,
52
41
  Presets = [],
53
42
  selectedPreset,
54
43
  onPresetChange,
55
- onSessionSelect,
56
44
  onNewSession,
57
- beta,
58
- storedSessions = [],
59
- refreshSessions = () => {},
60
- clearAllSessions = () => {},
61
45
  toolRenderers,
62
46
  onToolDecision,
63
47
  embedded,
@@ -66,20 +50,12 @@ const SidebarChat = ({
66
50
  const [aiResponded] = useEvent("AI_RESPONDED", null);
67
51
  const [conversationSent] = useEvent("CONVERSATION_SENT", null);
68
52
 
69
- const currentSessionId = sessionId || selectedConversationId;
70
-
71
53
  const [mute, setMute] = useState(false);
72
54
  const [loading, setLoading] = useState(false);
73
- const [unreadCount, setUnreadCount] = useState(0);
74
55
  const [play] = useSound(MessageSfx);
75
56
 
76
57
  const messagesEndRef = useRef(null);
77
58
  const highlightedMessage = useRef(null);
78
- const lastSeenCountRef = useRef(0);
79
- const sidebarRef = useRef<HTMLDivElement>(null);
80
-
81
- const [popoverAnchor, setPopoverAnchor] = useState<HTMLElement | null>(null);
82
- const [activeLogSessionId, setActiveLogSessionId] = useState("");
83
59
 
84
60
  const scrollToBottom = useCallback(() => {
85
61
  highlightedMessage.current?.scrollIntoView({
@@ -88,20 +64,6 @@ const SidebarChat = ({
88
64
  });
89
65
  }, []);
90
66
 
91
- useEffect(() => {
92
- if (open) {
93
- lastSeenCountRef.current = history.length;
94
- setUnreadCount(0);
95
- setPopoverAnchor(null);
96
- }
97
- }, [open, history.length]);
98
-
99
- useEffect(() => {
100
- if (!open && history.length > lastSeenCountRef.current) {
101
- setUnreadCount(history.length - lastSeenCountRef.current);
102
- }
103
- }, [history, open]);
104
-
105
67
  useEffect(() => {
106
68
  if (open) {
107
69
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
@@ -144,86 +106,29 @@ const SidebarChat = ({
144
106
  return false;
145
107
  }, [conversationSent, aiResponded, loading]);
146
108
 
147
- const handleSessionClick = useCallback(
148
- (event: React.MouseEvent<HTMLElement>, sid: string) => {
149
- refreshSessions();
150
- setActiveLogSessionId(sid);
151
- setPopoverAnchor(event.currentTarget);
152
- },
153
- [refreshSessions]
154
- );
155
-
156
- const handleOpenFromPopover = useCallback(() => {
157
- setPopoverAnchor(null);
158
- if (activeLogSessionId && activeLogSessionId !== currentSessionId) {
159
- const cached = storedSessions.find(
160
- (s) => s.sessionId === activeLogSessionId
161
- );
162
- onSessionSelect?.(activeLogSessionId, cached?.messages);
163
- }
164
- if (!open) handleToggle();
165
- }, [
166
- handleToggle,
167
- open,
168
- activeLogSessionId,
169
- currentSessionId,
170
- onSessionSelect,
171
- storedSessions,
172
- ]);
173
-
174
109
  return (
175
- <>
176
- {!open && !embedded && (
177
- <SidebarSessionList
178
- sessions={storedSessions}
179
- currentSessionId={currentSessionId}
180
- unreadCount={unreadCount}
181
- onToggleChat={handleToggle}
182
- onSessionClick={handleSessionClick}
183
- onClearAll={clearAllSessions}
184
- wrapperRef={sidebarRef}
185
- beta={beta}
186
- onNewSession={onNewSession}
187
- />
188
- )}
189
-
190
- {!embedded && (
191
- <SessionPopover
192
- anchorEl={popoverAnchor}
193
- onClose={() => setPopoverAnchor(null)}
194
- onOpenFullChat={handleOpenFromPopover}
195
- sessions={storedSessions}
196
- activeSessionId={activeLogSessionId}
197
- currentSessionId={currentSessionId}
198
- selectedConversationId={selectedConversationId}
199
- messagesEndRef={messagesEndRef}
200
- highlightedMessage={highlightedMessage}
201
- />
202
- )}
203
-
204
- <ChatDrawer
205
- title={title}
206
- open={open}
207
- onClose={handleToggle}
208
- history={history}
209
- selectedConversationId={selectedConversationId}
210
- readOnly={readOnly}
211
- mute={mute}
212
- onMuteToggle={() => setMute((prev) => !prev)}
213
- showLoading={showLoading()}
214
- onSend={handleSend}
215
- Presets={Presets}
216
- selectedPreset={selectedPreset}
217
- onPresetChange={onPresetChange}
218
- messagesEndRef={messagesEndRef}
219
- highlightedMessage={highlightedMessage}
220
- onNewSession={onNewSession}
221
- toolRenderers={toolRenderers}
222
- onToolDecision={onToolDecision}
223
- embedded={embedded}
224
- footer={footer}
225
- />
226
- </>
110
+ <ChatDrawer
111
+ title={title}
112
+ open={open}
113
+ onClose={handleToggle}
114
+ history={history}
115
+ selectedConversationId={selectedConversationId}
116
+ readOnly={readOnly}
117
+ mute={mute}
118
+ onMuteToggle={() => setMute((prev) => !prev)}
119
+ showLoading={showLoading()}
120
+ onSend={handleSend}
121
+ Presets={Presets}
122
+ selectedPreset={selectedPreset}
123
+ onPresetChange={onPresetChange}
124
+ messagesEndRef={messagesEndRef}
125
+ highlightedMessage={highlightedMessage}
126
+ onNewSession={onNewSession}
127
+ toolRenderers={toolRenderers}
128
+ onToolDecision={onToolDecision}
129
+ embedded={embedded}
130
+ footer={footer}
131
+ />
227
132
  );
228
133
  };
229
134
 
@@ -1,12 +1,20 @@
1
+ import DevToolFrame from "../DevTool/DevToolFrame";
2
+ import { Iconify } from "@canmingir/link/platform/components";
1
3
  import type { StoredSession } from "./types";
2
4
  import { alpha } from "@mui/material/styles";
3
5
  import { cleanIconName } from "./cleanIconName";
4
6
  import { publish } from "@nucleoidai/react-event";
5
7
 
6
8
  import { Badge, Box, Tooltip, Typography } from "@mui/material";
7
- import { DevTool, Iconify } from "@canmingir/link/platform/components";
8
9
  import React, { memo } from "react";
9
10
 
11
+ export interface DevToolTopAction {
12
+ icon: string;
13
+ label: string;
14
+ onClick: () => void;
15
+ tooltip?: string;
16
+ }
17
+
10
18
  interface SidebarSessionListProps {
11
19
  sessions: StoredSession[];
12
20
  currentSessionId?: string;
@@ -20,6 +28,7 @@ interface SidebarSessionListProps {
20
28
  onNewSession?: () => void;
21
29
  wrapperRef?: React.Ref<HTMLDivElement>;
22
30
  beta?: boolean;
31
+ topAction?: DevToolTopAction;
23
32
  }
24
33
 
25
34
  const btnBase = {
@@ -47,9 +56,48 @@ const SidebarSessionList = ({
47
56
  onNewSession,
48
57
  wrapperRef,
49
58
  beta,
59
+ topAction,
50
60
  }: SidebarSessionListProps) => {
51
61
  const sidebarSessions = sessions;
52
62
 
63
+ const contextRailContent = topAction ? (
64
+ <Tooltip
65
+ title={topAction.tooltip ?? topAction.label}
66
+ placement="left"
67
+ enterDelay={1000}
68
+ enterNextDelay={1000}
69
+ >
70
+ <Box
71
+ onClick={topAction.onClick}
72
+ sx={{
73
+ ...btnBase,
74
+ flexDirection: "column",
75
+ gap: 0.5,
76
+ color: "text.secondary",
77
+ "&:hover": {
78
+ color: "primary.main",
79
+ bgcolor: (theme) => alpha(theme.palette.primary.main, 0.12),
80
+ borderColor: (theme) => alpha(theme.palette.primary.main, 0.2),
81
+ transform: "scale(1.08)",
82
+ },
83
+ }}
84
+ >
85
+ <Iconify icon={topAction.icon} sx={{ width: 20, height: 20 }} />
86
+ <Typography
87
+ sx={{
88
+ fontSize: "0.5rem",
89
+ fontWeight: 500,
90
+ letterSpacing: 0.5,
91
+ lineHeight: 1,
92
+ textTransform: "capitalize",
93
+ }}
94
+ >
95
+ {topAction.label}
96
+ </Typography>
97
+ </Box>
98
+ </Tooltip>
99
+ ) : null;
100
+
53
101
  const header = (
54
102
  <>
55
103
  <Tooltip
@@ -291,13 +339,28 @@ const SidebarSessionList = ({
291
339
  );
292
340
 
293
341
  return (
294
- <DevTool
295
- width={45}
296
- height={310}
297
- header={header}
298
- content={content}
299
- footer={footer}
300
- />
342
+ <>
343
+ {contextRailContent && (
344
+ <DevToolFrame
345
+ width={45}
346
+ height="auto"
347
+ content={contextRailContent}
348
+ sx={{
349
+ top: "calc(50% - 155px)",
350
+ transform: "translateY(calc(-100% - 12px))",
351
+ py: 1,
352
+ }}
353
+ />
354
+ )}
355
+
356
+ <DevToolFrame
357
+ width={45}
358
+ height={310}
359
+ header={header}
360
+ content={content}
361
+ footer={footer}
362
+ />
363
+ </>
301
364
  );
302
365
  };
303
366