@blocklet/aigne-hub 0.4.42 → 0.4.44

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.
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./chat"), exports);
18
18
  __exportStar(require("./image"), exports);
19
19
  __exportStar(require("./embedding"), exports);
20
+ __exportStar(require("./model"), exports);
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -12,7 +12,7 @@ const alert_1 = __importDefault(require("../credit/alert"));
12
12
  const image_preview_1 = __importDefault(require("../image-preview"));
13
13
  const message_1 = __importDefault(require("./message"));
14
14
  const prompt_1 = __importDefault(require("./prompt"));
15
- function Conversation({ ref, messages, onSubmit, customActions = () => [], renderAvatar = undefined, maxWidth = 1000, scrollContainer = undefined, promptProps = {}, ...props }) {
15
+ function Conversation({ ref, messages, onSubmit, customActions = () => [], renderAvatar = undefined, maxWidth = 1000, scrollContainer = undefined, promptProps = {}, chatLayout = 'left-right', ...props }) {
16
16
  const scroller = (0, react_1.useRef)(scrollContainer !== null && scrollContainer !== void 0 ? scrollContainer : null);
17
17
  const { element, scrollToBottom } = useAutoScrollToBottom({ scroller });
18
18
  (0, react_1.useImperativeHandle)(ref, () => ({
@@ -24,28 +24,57 @@ function Conversation({ ref, messages, onSubmit, customActions = () => [], rende
24
24
  flexDirection: 'column',
25
25
  overflow: 'auto',
26
26
  ...props.sx,
27
- }, children: (0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { mt: 2, mx: 2, flexGrow: 1, display: 'flex', flexDirection: 'column' }, className: "conversation-container", children: [(0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { flexGrow: 1, width: '100%', mx: 'auto', maxWidth }, children: [messages.map((msg) => {
27
+ }, children: (0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { mt: 3, mx: 2, flexGrow: 1, display: 'flex', flexDirection: 'column' }, className: "conversation-container", children: [(0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { flexGrow: 1, width: '100%', mx: 'auto', maxWidth }, children: [messages.map((msg) => {
28
28
  var _a, _b;
29
29
  const actions = customActions === null || customActions === void 0 ? void 0 : customActions(msg);
30
- return ((0, jsx_runtime_1.jsxs)(material_1.Box, { id: `conversation-${msg.id}`, children: [!(0, isNil_1.default)(msg.prompt) && ((0, jsx_runtime_1.jsx)(message_1.default, { avatar: (_a = renderAvatar === null || renderAvatar === void 0 ? void 0 : renderAvatar(msg, false)) !== null && _a !== void 0 ? _a : (0, jsx_runtime_1.jsx)(material_1.Avatar, { sx: { bgcolor: 'secondary.main' }, children: "\uD83E\uDDD1" }), message: msg.prompt, actions: actions === null || actions === void 0 ? void 0 : actions[0] })), (!(0, isNil_1.default)(msg.response) || !(0, isNil_1.default)(msg.loading) || !(0, isNil_1.default)(msg.error)) && ((0, jsx_runtime_1.jsxs)(message_1.default, { my: 1, id: `response-${msg.id}`, loading: msg.loading && !!msg.response, message: typeof msg.response === 'string' ? msg.response : undefined, avatar: (_b = renderAvatar === null || renderAvatar === void 0 ? void 0 : renderAvatar(msg, true)) !== null && _b !== void 0 ? _b : (0, jsx_runtime_1.jsx)(material_1.Avatar, { sx: { bgcolor: 'primary.main' }, children: "\uD83E\uDD16\uFE0F" }), actions: actions === null || actions === void 0 ? void 0 : actions[1], children: [Array.isArray(msg.response) && ((0, jsx_runtime_1.jsx)(image_preview_1.default, { itemWidth: 100, dataSource: msg.response.map(({ url }) => {
31
- return {
32
- src: url,
33
- onLoad: () => scrollToBottom(),
34
- };
35
- }) })), msg.error ? (
36
- // @ts-ignore
37
- (0, jsx_runtime_1.jsx)(alert_1.default, { error: msg.error })) : (msg.loading &&
38
- !msg.response && ((0, jsx_runtime_1.jsx)(material_1.Box, { sx: {
39
- minHeight: 24,
40
- display: 'flex',
41
- alignItems: 'center',
42
- }, children: (0, jsx_runtime_1.jsx)(material_1.CircularProgress, { size: 16 }) })))] }))] }, msg.id));
30
+ const isLeftRight = chatLayout === 'left-right';
31
+ return ((0, jsx_runtime_1.jsx)(material_1.Fade, { in: true, timeout: 300, children: (0, jsx_runtime_1.jsxs)(material_1.Box, { id: `conversation-${msg.id}`, children: [!(0, isNil_1.default)(msg.prompt) && ((0, jsx_runtime_1.jsx)(message_1.default, { avatar: (_a = renderAvatar === null || renderAvatar === void 0 ? void 0 : renderAvatar(msg, false)) !== null && _a !== void 0 ? _a : (0, jsx_runtime_1.jsx)(material_1.Avatar, { sx: { bgcolor: 'secondary.main' }, children: "\uD83E\uDDD1" }), message: msg.prompt, actions: actions === null || actions === void 0 ? void 0 : actions[0], timestamp: msg.timestamp, isUser: isLeftRight, chatLayout: chatLayout })), (!(0, isNil_1.default)(msg.response) || !(0, isNil_1.default)(msg.loading) || !(0, isNil_1.default)(msg.error)) && ((0, jsx_runtime_1.jsxs)(message_1.default, { id: `response-${msg.id}`, loading: msg.loading && !!msg.response, message: typeof msg.response === 'string' ? msg.response : undefined, avatar: (_b = renderAvatar === null || renderAvatar === void 0 ? void 0 : renderAvatar(msg, true)) !== null && _b !== void 0 ? _b : (0, jsx_runtime_1.jsx)(material_1.Avatar, { sx: { bgcolor: 'primary.main' }, children: "\uD83E\uDD16\uFE0F" }), actions: actions === null || actions === void 0 ? void 0 : actions[1], timestamp: msg.timestamp, isUser: false, chatLayout: chatLayout, children: [msg.response &&
32
+ typeof msg.response === 'object' &&
33
+ 'images' in msg.response &&
34
+ Array.isArray(msg.response.images) &&
35
+ msg.response.images.length > 0 && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [msg.response.images.some((img) => img.url && img.url.startsWith('data:')) && ((0, jsx_runtime_1.jsx)(image_preview_1.default, { itemWidth: 200, borderRadius: 12, dataSource: msg.response.images
36
+ .filter((img) => img.url && img.url.startsWith('data:'))
37
+ .map(({ url }) => ({
38
+ src: url,
39
+ onLoad: () => scrollToBottom(),
40
+ })) })), msg.response.images.some((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]') && ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: {
41
+ margin: '8px 0',
42
+ minHeight: '200px',
43
+ background: '#f5f5f5',
44
+ borderRadius: '8px',
45
+ padding: '16px',
46
+ display: 'flex',
47
+ alignItems: 'center',
48
+ justifyContent: 'center',
49
+ flexDirection: 'column',
50
+ gap: '12px',
51
+ border: '2px dashed #ddd',
52
+ }, children: [(0, jsx_runtime_1.jsx)(material_1.Box, { sx: { fontSize: '48px', opacity: 0.4 }, children: "\uD83D\uDDBC\uFE0F" }), (0, jsx_runtime_1.jsx)(material_1.Box, { sx: {
53
+ fontSize: '14px',
54
+ color: '#666',
55
+ textAlign: 'center',
56
+ fontWeight: 500,
57
+ minWidth: 200,
58
+ }, children: msg.response.images.filter((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]')
59
+ .length === 1
60
+ ? 'Image (Not Cached)'
61
+ : `${msg.response.images.filter((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]').length} Images (Not Cached)` })] }))] })), msg.error ? (
62
+ // @ts-ignore
63
+ (0, jsx_runtime_1.jsx)(alert_1.default, { error: msg.error })) : (msg.loading &&
64
+ !msg.response && ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: {
65
+ minHeight: 32,
66
+ display: 'flex',
67
+ alignItems: 'center',
68
+ gap: 1.5,
69
+ color: 'text.secondary',
70
+ fontSize: '14px',
71
+ }, children: [(0, jsx_runtime_1.jsx)(material_1.CircularProgress, { size: 18, thickness: 4 }), (0, jsx_runtime_1.jsx)("span", { children: "AI is thinking..." })] })))] }))] }) }, msg.id));
43
72
  }), element] }), (0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { mx: 'auto', width: '100%', maxWidth, position: 'sticky', bottom: 0 }, children: [(0, jsx_runtime_1.jsx)(material_1.Box, { sx: {
44
- height: 16,
73
+ height: 24,
45
74
  pointerEvents: 'none',
46
75
  background: (theme) => `linear-gradient(transparent, ${theme.palette.background.paper})`,
47
76
  } }), (0, jsx_runtime_1.jsx)(material_1.Box, { sx: {
48
- pb: 2,
77
+ pb: 4,
49
78
  bgcolor: 'background.paper',
50
79
  }, children: (0, jsx_runtime_1.jsx)(prompt_1.default, { onSubmit: onSubmit, ...promptProps }) })] })] }) }));
51
80
  }
@@ -6,115 +6,318 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.default = Message;
7
7
  const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const css_1 = require("@emotion/css");
9
- const styled_1 = __importDefault(require("@emotion/styled"));
10
9
  const icons_material_1 = require("@mui/icons-material");
11
10
  const material_1 = require("@mui/material");
12
11
  const react_1 = require("react");
13
12
  const react_markdown_1 = __importDefault(require("react-markdown"));
14
- function Message({ avatar = undefined, message = undefined, children = undefined, loading = false, actions = undefined, ...props }) {
13
+ function Message({ avatar = undefined, message = undefined, children = undefined, loading = false, actions = undefined, timestamp = undefined, isUser = false, chatLayout = 'traditional', ...props }) {
14
+ const theme = (0, material_1.useTheme)();
15
15
  const text = (0, react_1.useMemo)(() => (typeof message === 'string' ? message : message === null || message === void 0 ? void 0 : message.map((i) => `${i.role}: ${i.content}`).join('\n\n')), [message]);
16
- return ((0, jsx_runtime_1.jsxs)(Root, { ...props, display: "flex", children: [(0, jsx_runtime_1.jsx)(material_1.Box, { className: "avatar", sx: {
17
- mr: 1,
18
- }, children: avatar }), (0, jsx_runtime_1.jsxs)(material_1.Box, { className: (0, css_1.cx)('content'), children: [(0, jsx_runtime_1.jsx)(material_1.Box, { component: react_markdown_1.default, className: (0, css_1.cx)('message', loading && 'cursor'), children: text }), children, (0, jsx_runtime_1.jsxs)(material_1.Box, { className: "actions", children: [actions, text && (0, jsx_runtime_1.jsx)(CopyButton, { message: text }, "copy")] })] })] }));
16
+ // Create theme-based styles
17
+ const getMessageStyles = () => {
18
+ const baseStyles = {
19
+ '> .message-content-wrapper': {
20
+ '> .content': {
21
+ '> .message': {
22
+ lineHeight: 1.6,
23
+ fontSize: '15px',
24
+ '> *:first-of-type': {
25
+ marginTop: 0,
26
+ },
27
+ '> *:last-child': {
28
+ marginBottom: 0,
29
+ },
30
+ pre: {
31
+ lineHeight: 1.5,
32
+ backgroundColor: theme.palette.grey[50],
33
+ overflow: 'auto',
34
+ padding: 2,
35
+ borderRadius: 1,
36
+ border: `1px solid ${theme.palette.divider}`,
37
+ margin: '12px 0',
38
+ boxShadow: theme.shadows[1],
39
+ position: 'relative',
40
+ '&::before': {
41
+ content: 'attr(data-language)',
42
+ position: 'absolute',
43
+ top: 8,
44
+ right: 8,
45
+ fontSize: '11px',
46
+ color: theme.palette.text.disabled,
47
+ textTransform: 'uppercase',
48
+ fontWeight: 600,
49
+ letterSpacing: '0.5px',
50
+ },
51
+ },
52
+ code: {
53
+ backgroundColor: theme.palette.action.hover,
54
+ padding: '2px 6px',
55
+ borderRadius: 0.5,
56
+ fontSize: '0.9em',
57
+ fontFamily: '"Monaco", "Menlo", "Ubuntu Mono", "Consolas", "source-code-pro", monospace',
58
+ },
59
+ 'pre code': {
60
+ backgroundColor: 'transparent',
61
+ padding: 0,
62
+ display: 'block',
63
+ border: 'none !important',
64
+ },
65
+ 'ul, ol': {
66
+ paddingLeft: '24px',
67
+ },
68
+ li: {
69
+ margin: '4px 0',
70
+ },
71
+ blockquote: {
72
+ borderLeft: `3px solid ${theme.palette.divider}`,
73
+ paddingLeft: 2,
74
+ margin: '12px 0',
75
+ color: theme.palette.text.secondary,
76
+ },
77
+ table: {
78
+ borderCollapse: 'collapse',
79
+ width: '100%',
80
+ margin: '12px 0',
81
+ },
82
+ 'th, td': {
83
+ border: `1px solid ${theme.palette.divider}`,
84
+ padding: '8px 12px',
85
+ textAlign: 'left',
86
+ },
87
+ th: {
88
+ backgroundColor: theme.palette.action.hover,
89
+ fontWeight: 600,
90
+ },
91
+ '&.cursor': {
92
+ '> *:last-child': {
93
+ '&:after': {
94
+ content: '""',
95
+ display: 'inline-block',
96
+ verticalAlign: 'middle',
97
+ height: '1em',
98
+ marginTop: '-0.15em',
99
+ marginLeft: '0.15em',
100
+ borderRight: `0.15em solid ${theme.palette.primary.main}`,
101
+ animation: 'blink-caret 0.75s step-end infinite',
102
+ '@keyframes blink-caret': {
103
+ 'from, to': {
104
+ borderColor: 'transparent',
105
+ },
106
+ '50%': {
107
+ borderColor: theme.palette.primary.main,
108
+ },
109
+ },
110
+ },
111
+ },
112
+ },
113
+ },
114
+ },
115
+ },
116
+ };
117
+ // User message styles (right-aligned with blue background)
118
+ if (chatLayout === 'left-right' && isUser) {
119
+ return {
120
+ ...baseStyles,
121
+ '> .message-content-wrapper > .content': {
122
+ background: theme.palette.primary.light,
123
+ color: theme.palette.primary.contrastText,
124
+ border: 'none',
125
+ boxShadow: theme.shadows[2],
126
+ position: 'relative',
127
+ overflow: 'visible',
128
+ '.message': {
129
+ color: theme.palette.primary.contrastText,
130
+ code: {
131
+ backgroundColor: 'rgba(255, 255, 255, 0.25)',
132
+ color: theme.palette.primary.contrastText,
133
+ border: '1px solid rgba(255, 255, 255, 0.15)',
134
+ },
135
+ pre: {
136
+ backgroundColor: 'rgba(0, 0, 0, 0.25)',
137
+ borderColor: 'rgba(255, 255, 255, 0.15)',
138
+ boxShadow: 'inset 0 1px 3px rgba(0, 0, 0, 0.2)',
139
+ code: {
140
+ color: 'rgba(255, 255, 255, 0.95)',
141
+ border: 'none !important',
142
+ backgroundColor: 'transparent !important',
143
+ padding: '0 !important',
144
+ },
145
+ },
146
+ a: {
147
+ color: theme.palette.primary.light,
148
+ textDecoration: 'underline',
149
+ textDecorationColor: 'rgba(255, 255, 255, 0.4)',
150
+ '&:hover': {
151
+ color: '#bbdefb',
152
+ },
153
+ },
154
+ strong: {
155
+ fontWeight: 600,
156
+ color: 'rgba(255, 255, 255, 0.98)',
157
+ },
158
+ },
159
+ },
160
+ '&:hover > .message-content-wrapper > .content': {
161
+ background: theme.palette.primary.main,
162
+ },
163
+ };
164
+ }
165
+ // AI message styles (left-aligned with light background)
166
+ return {
167
+ ...baseStyles,
168
+ '> .message-content-wrapper > .content': {
169
+ background: theme.palette.background.paper,
170
+ border: `1px solid ${theme.palette.divider}`,
171
+ boxShadow: theme.shadows[1],
172
+ '.message': {
173
+ color: theme.palette.text.primary,
174
+ code: {
175
+ background: theme.palette.grey[100],
176
+ border: `1px solid ${theme.palette.divider}`,
177
+ },
178
+ pre: {
179
+ background: theme.palette.grey[50],
180
+ border: `1px solid ${theme.palette.divider}`,
181
+ code: {
182
+ background: 'transparent !important',
183
+ border: 'none !important',
184
+ padding: '0 !important',
185
+ },
186
+ },
187
+ a: {
188
+ color: theme.palette.primary.main,
189
+ textDecoration: 'none',
190
+ borderBottom: `1px solid ${theme.palette.primary.light}`,
191
+ transition: 'all 0.2s ease',
192
+ '&:hover': {
193
+ color: theme.palette.primary.dark,
194
+ borderBottomColor: theme.palette.primary.dark,
195
+ },
196
+ },
197
+ strong: {
198
+ fontWeight: 600,
199
+ color: theme.palette.text.primary,
200
+ },
201
+ },
202
+ },
203
+ '&:hover > .message-content-wrapper > .content': {
204
+ background: theme.palette.grey[50],
205
+ borderColor: theme.palette.action.focus,
206
+ },
207
+ };
208
+ };
209
+ // Force re-render every minute to update relative time
210
+ const [now, setNow] = (0, react_1.useState)(Date.now());
211
+ (0, react_1.useEffect)(() => {
212
+ if (!timestamp)
213
+ return undefined;
214
+ const interval = setInterval(() => {
215
+ setNow(Date.now());
216
+ }, 60000); // Update every minute
217
+ return () => clearInterval(interval);
218
+ }, [timestamp]);
219
+ const formattedTime = (0, react_1.useMemo)(() => {
220
+ if (!timestamp)
221
+ return '';
222
+ const date = new Date(timestamp);
223
+ const diffMs = now - date.getTime();
224
+ const diffMins = Math.floor(diffMs / 60000);
225
+ const diffHours = Math.floor(diffMs / 3600000);
226
+ const diffDays = Math.floor(diffMs / 86400000);
227
+ if (diffMins < 1)
228
+ return 'Just now';
229
+ if (diffMins < 60)
230
+ return `${diffMins}m ago`;
231
+ if (diffHours < 24)
232
+ return `${diffHours}h ago`;
233
+ if (diffDays < 7)
234
+ return `${diffDays}d ago`;
235
+ return date.toLocaleDateString();
236
+ }, [timestamp, now]);
237
+ const isLeftRight = chatLayout === 'left-right';
238
+ return ((0, jsx_runtime_1.jsxs)(material_1.Box, { ...props, display: "flex", className: (0, css_1.cx)(isLeftRight && isUser && 'user-message', isLeftRight && !isUser && 'ai-message'), sx: {
239
+ mb: 2.5,
240
+ '&:hover .message-meta': {
241
+ opacity: 1,
242
+ },
243
+ ...(isLeftRight && isUser
244
+ ? {
245
+ justifyContent: 'flex-end',
246
+ '.avatar': { order: 2, mr: 0, ml: 1 },
247
+ '.content': { alignItems: 'flex-end' },
248
+ }
249
+ : {}),
250
+ ...getMessageStyles(),
251
+ ...props.sx,
252
+ }, children: [(0, jsx_runtime_1.jsx)(material_1.Box, { className: "avatar", sx: {
253
+ pt: 0.625,
254
+ flexShrink: 0,
255
+ mr: isLeftRight && !isUser ? 1 : isLeftRight && isUser ? 0 : 1,
256
+ ml: isLeftRight && isUser ? 1 : 0,
257
+ '& .MuiAvatar-root': {
258
+ width: 38,
259
+ height: 38,
260
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.12)',
261
+ },
262
+ }, children: avatar }), (0, jsx_runtime_1.jsxs)(material_1.Box, { className: "message-content-wrapper", sx: {
263
+ display: 'flex',
264
+ flexDirection: 'column',
265
+ maxWidth: '80%',
266
+ minWidth: 'auto',
267
+ position: 'relative',
268
+ }, children: [(0, jsx_runtime_1.jsxs)(material_1.Box, { className: (0, css_1.cx)('content'), sx: {
269
+ minHeight: 40,
270
+ overflow: 'hidden',
271
+ wordBreak: 'break-word',
272
+ padding: 1.75,
273
+ borderRadius: 2,
274
+ position: 'relative',
275
+ backgroundColor: 'transparent',
276
+ border: 'none',
277
+ transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',
278
+ display: 'flex',
279
+ flexDirection: 'column',
280
+ }, children: [text && ((0, jsx_runtime_1.jsx)(material_1.Box, { component: react_markdown_1.default, className: (0, css_1.cx)('message', loading && 'cursor'), children: text })), children] }), (0, jsx_runtime_1.jsxs)(material_1.Box, { className: "message-meta", sx: {
281
+ display: 'flex',
282
+ alignItems: 'center',
283
+ gap: 1,
284
+ mt: 0.5,
285
+ opacity: 0,
286
+ transition: 'opacity 0.2s ease',
287
+ justifyContent: isLeftRight && isUser ? 'flex-end' : 'flex-start',
288
+ }, children: [timestamp && ((0, jsx_runtime_1.jsx)(material_1.Box, { className: "timestamp", sx: {
289
+ fontSize: '11px',
290
+ color: 'text.secondary',
291
+ }, children: formattedTime })), (0, jsx_runtime_1.jsxs)(material_1.Box, { className: "actions", sx: {
292
+ display: 'flex',
293
+ gap: 0.5,
294
+ '& button': {
295
+ minWidth: 0,
296
+ p: 0.5,
297
+ height: 24,
298
+ width: 24,
299
+ color: 'text.secondary',
300
+ borderRadius: 0.5,
301
+ transition: 'all 0.15s ease',
302
+ },
303
+ }, children: [actions, text && (0, jsx_runtime_1.jsx)(CopyButton, { message: text }, "copy")] })] })] })] }));
19
304
  }
20
305
  function CopyButton({ message }) {
21
306
  const [copied, setCopied] = (0, react_1.useState)(false);
22
- return ((0, jsx_runtime_1.jsx)(material_1.Tooltip, { title: copied === 'copied' ? 'Copied!' : 'Copy', placement: "top", open: Boolean(copied), children: (0, jsx_runtime_1.jsx)(material_1.Button, { size: "small", className: (0, css_1.cx)('copy', copied && 'active'), onMouseEnter: () => setCopied(true), onMouseLeave: () => setCopied(false), onClick: () => {
23
- navigator.clipboard.writeText(message);
24
- setCopied('copied');
25
- setTimeout(() => setCopied(false), 1500);
26
- }, children: (0, jsx_runtime_1.jsx)(icons_material_1.CopyAll, { fontSize: "small" }) }) }));
307
+ const [showTooltip, setShowTooltip] = (0, react_1.useState)(false);
308
+ return ((0, jsx_runtime_1.jsx)(material_1.Tooltip, { title: copied === 'copied' ? 'Copied!' : 'Copy', placement: "top", open: showTooltip || Boolean(copied), children: (0, jsx_runtime_1.jsx)(material_1.Button, { size: "small", className: (0, css_1.cx)('copy', copied && 'active'), onMouseEnter: () => setShowTooltip(true), onMouseLeave: () => setShowTooltip(false), onClick: () => {
309
+ navigator.clipboard
310
+ .writeText(message)
311
+ .then(() => {
312
+ setCopied('copied');
313
+ })
314
+ .catch((err) => {
315
+ console.error('Failed to copy message', err);
316
+ });
317
+ setShowTooltip(false);
318
+ setTimeout(() => setCopied(false), 2000);
319
+ }, sx: {
320
+ color: copied === 'copied' ? 'success.main' : 'inherit',
321
+ transition: 'color 0.2s ease',
322
+ }, children: copied === 'copied' ? (0, jsx_runtime_1.jsx)(icons_material_1.CheckCircleOutline, { fontSize: "small" }) : (0, jsx_runtime_1.jsx)(icons_material_1.CopyAll, { fontSize: "small" }) }) }));
27
323
  }
28
- const Root = (0, styled_1.default)(material_1.Box) `
29
- > .avatar {
30
- padding-top: 5px;
31
-
32
- > .MuiAvatar-root {
33
- width: 30px;
34
- height: 30px;
35
- }
36
- }
37
-
38
- > .content {
39
- min-height: 40px;
40
- flex: 1;
41
- overflow: hidden;
42
- word-break: break-word;
43
- padding: 8px;
44
- border-radius: 4px;
45
- position: relative;
46
-
47
- > .message {
48
- > *:first-of-type {
49
- margin-top: 0;
50
- }
51
- > *:last-child {
52
- margin-bottom: 0;
53
- }
54
-
55
- pre {
56
- line-height: 1.2;
57
- background-color: #f6f8fa;
58
- overflow: auto;
59
- padding: 16px;
60
- border-radius: 3px;
61
- }
62
-
63
- &.cursor {
64
- > *:last-child {
65
- &:after {
66
- content: '';
67
- display: inline-block;
68
- vertical-align: middle;
69
- height: 1em;
70
- margin-top: -0.15em;
71
- margin-left: 0.15em;
72
- border-right: 0.15em solid orange;
73
- animation: blink-caret 0.75s step-end infinite;
74
-
75
- @keyframes blink-caret {
76
- from,
77
- to {
78
- border-color: transparent;
79
- }
80
- 50% {
81
- border-color: orange;
82
- }
83
- }
84
- }
85
- }
86
- }
87
- }
88
-
89
- > .actions {
90
- position: absolute;
91
- right: 2px;
92
- top: 2px;
93
- border-radius: 4px;
94
- opacity: 0;
95
-
96
- &.active {
97
- display: flex;
98
- }
99
-
100
- button {
101
- min-width: 0;
102
- padding: 0;
103
- height: 24px;
104
- width: 22px;
105
- color: rgba(0, 0, 0, 0.4);
106
- }
107
- }
108
- }
109
-
110
- &:hover {
111
- > .content {
112
- background-color: rgba(0, 0, 0, 0.05);
113
-
114
- > .actions {
115
- opacity: 1;
116
- background-color: rgba(240, 240, 240, 0.9);
117
- }
118
- }
119
- }
120
- `;
@@ -6,8 +6,9 @@ const icons_material_1 = require("@mui/icons-material");
6
6
  const material_1 = require("@mui/material");
7
7
  const ahooks_1 = require("ahooks");
8
8
  const react_1 = require("react");
9
- function Prompt({ startAdornment = undefined, endAdornment = undefined, onSubmit, slotProps = {}, sx = {}, ...props }) {
9
+ function Prompt({ startAdornment = undefined, endAdornment = undefined, topAdornment = undefined, onSubmit, slotProps = {}, sx = {}, placeholder = 'Type your message... (Shift+Enter for new line)', ...props }) {
10
10
  const [prompt, setPrompt] = (0, react_1.useState)('');
11
+ const [isFocused, setIsFocused] = (0, react_1.useState)(false);
11
12
  const { value: historyPrompt, setValue: setHistoryPrompt, forwardLength, back, go, forward } = (0, ahooks_1.useHistoryTravel)('');
12
13
  const submit = () => {
13
14
  if (!prompt.trim()) {
@@ -21,23 +22,87 @@ function Prompt({ startAdornment = undefined, endAdornment = undefined, onSubmit
21
22
  setPrompt('');
22
23
  }, 50);
23
24
  };
24
- return ((0, jsx_runtime_1.jsxs)(material_1.Box, { ...props, sx: { display: 'flex', gap: 1, alignItems: 'center', ...sx }, component: "form", onSubmit: (e) => e.preventDefault(), children: [startAdornment, (0, jsx_runtime_1.jsx)(material_1.Input, { fullWidth: true, disableUnderline: true, value: prompt, multiline: true, maxRows: 10, sx: { py: 0.8, px: 1, boxShadow: 2, borderRadius: 1 }, onChange: (e) => setPrompt(e.target.value), onKeyDown: (e) => {
25
- if (e.keyCode === 229) {
26
- return;
27
- }
28
- if (!e.shiftKey && e.key === 'Enter') {
29
- e.preventDefault();
30
- submit();
31
- }
32
- else if (e.key === 'ArrowUp') {
33
- e.preventDefault();
34
- back();
35
- setPrompt(historyPrompt || '');
36
- }
37
- else if (e.key === 'ArrowDown') {
38
- e.preventDefault();
39
- forward();
40
- setPrompt(historyPrompt || '');
41
- }
42
- }, endAdornment: (0, jsx_runtime_1.jsx)(material_1.InputAdornment, { position: "end", children: (0, jsx_runtime_1.jsx)(material_1.IconButton, { onClick: submit, size: "small", type: "submit", children: (0, jsx_runtime_1.jsx)(icons_material_1.Send, { fontSize: "small" }) }) }), ...slotProps }), endAdornment] }));
25
+ const charCount = prompt.length;
26
+ const showCharCount = isFocused && charCount > 0;
27
+ return ((0, jsx_runtime_1.jsxs)(material_1.Box, { ...props, sx: { display: 'flex', flexDirection: 'column', gap: 1.5, ...sx }, component: "form", onSubmit: (e) => e.preventDefault(), children: [topAdornment && ((0, jsx_runtime_1.jsx)(material_1.Box, { sx: {
28
+ display: 'flex',
29
+ alignItems: 'center',
30
+ justifyContent: 'space-between',
31
+ gap: 2,
32
+ px: 1,
33
+ }, children: topAdornment })), (0, jsx_runtime_1.jsxs)(material_1.Box, { sx: {
34
+ position: 'relative',
35
+ flex: 1,
36
+ display: 'flex',
37
+ alignItems: 'stretch',
38
+ gap: 1.5,
39
+ p: 1.5,
40
+ boxShadow: '0 2px 12px rgba(0, 0, 0, 0.08)',
41
+ borderRadius: 2,
42
+ border: '1px solid',
43
+ borderColor: 'divider',
44
+ transition: 'all 0.2s ease',
45
+ bgcolor: 'background.paper',
46
+ '&:hover': {
47
+ boxShadow: '0 4px 16px rgba(0, 0, 0, 0.12)',
48
+ borderColor: 'primary.main',
49
+ },
50
+ '&:focus-within': {
51
+ boxShadow: '0 4px 20px rgba(25, 118, 210, 0.2)',
52
+ borderColor: 'primary.main',
53
+ },
54
+ }, children: [startAdornment, (0, jsx_runtime_1.jsx)(material_1.Input, { fullWidth: true, disableUnderline: true, value: prompt, multiline: true, maxRows: 10, placeholder: placeholder, sx: {
55
+ py: 0,
56
+ px: 0,
57
+ fontSize: '15px',
58
+ border: 'none',
59
+ boxShadow: 'none',
60
+ '&:hover': {
61
+ boxShadow: 'none',
62
+ },
63
+ '&.Mui-focused': {
64
+ boxShadow: 'none',
65
+ },
66
+ }, onChange: (e) => setPrompt(e.target.value), onFocus: () => setIsFocused(true), onBlur: () => setIsFocused(false), onKeyDown: (e) => {
67
+ if (e.keyCode === 229) {
68
+ return;
69
+ }
70
+ if (!e.shiftKey && e.key === 'Enter') {
71
+ e.preventDefault();
72
+ submit();
73
+ }
74
+ else if (e.key === 'ArrowUp') {
75
+ e.preventDefault();
76
+ back();
77
+ setPrompt(historyPrompt || '');
78
+ }
79
+ else if (e.key === 'ArrowDown') {
80
+ e.preventDefault();
81
+ forward();
82
+ setPrompt(historyPrompt || '');
83
+ }
84
+ }, ...slotProps }), (0, jsx_runtime_1.jsx)(material_1.IconButton, { onClick: submit, size: "medium", type: "submit", disabled: !prompt.trim(), sx: {
85
+ bgcolor: prompt.trim() ? 'primary.main' : 'action.disabledBackground',
86
+ color: prompt.trim() ? 'primary.contrastText' : 'action.disabled',
87
+ transition: 'all 0.2s ease',
88
+ width: 44,
89
+ height: 44,
90
+ alignSelf: 'flex-end',
91
+ flexShrink: 0,
92
+ '&:hover': {
93
+ bgcolor: prompt.trim() ? 'primary.dark' : 'action.disabledBackground',
94
+ transform: prompt.trim() ? 'scale(1.05)' : 'none',
95
+ },
96
+ '&.Mui-disabled': {
97
+ bgcolor: 'action.disabledBackground',
98
+ color: 'action.disabled',
99
+ },
100
+ }, children: (0, jsx_runtime_1.jsx)(icons_material_1.Send, { fontSize: "small" }) }), showCharCount && ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: {
101
+ position: 'absolute',
102
+ bottom: -24,
103
+ right: 8,
104
+ fontSize: '11px',
105
+ color: 'text.secondary',
106
+ opacity: 0.7,
107
+ }, children: [charCount, " characters"] }))] }), endAdornment] }));
43
108
  }