@nethru/kit 1.1.15 → 1.1.18

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.
@@ -5,19 +5,21 @@ import ChatMessages from './ChatMessages';
5
5
  import ChatInput from './input/ChatInput';
6
6
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  function AiChat({
8
- mainPageRef,
8
+ pageRef,
9
+ pagePath,
9
10
  sx
10
11
  }) {
11
12
  const {
12
13
  messages,
13
14
  isLoading,
14
15
  chatContainerRef,
15
- mainPageRef: _mainPageRef
16
+ pageRef: _pageRef,
17
+ setPagePath
16
18
  } = useChatContext();
17
19
  useEffect(() => {
18
- if (!mainPageRef) return;
19
- _mainPageRef.current = mainPageRef.current;
20
- }, [mainPageRef?.current]);
20
+ if (pageRef) _pageRef.current = pageRef.current;
21
+ setPagePath(pagePath);
22
+ }, [pageRef?.current, pagePath]);
21
23
  return /*#__PURE__*/_jsxs(Stack, {
22
24
  sx: {
23
25
  ...styles.container,
@@ -1,8 +1,9 @@
1
1
  import React, { useMemo } from 'react';
2
- import { Box, keyframes } from '@mui/material';
2
+ import { Box, IconButton, keyframes, Stack, Tooltip } from '@mui/material';
3
3
  import MarkdownContent from "./content/MarkdownContent";
4
4
  import ToolContent from "./content/ToolContent";
5
5
  import { ToolContextProvider } from "./contexts/ToolContext";
6
+ import ReplayRoundedIcon from "@mui/icons-material/ReplayRounded";
6
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
8
  const slideIn = keyframes`
8
9
  from {
@@ -15,20 +16,43 @@ const slideIn = keyframes`
15
16
  }
16
17
  `;
17
18
  const ChatMessage = ({
18
- message
19
+ message,
20
+ retryable,
21
+ onRetry
19
22
  }) => {
20
23
  const textContent = useMemo(() => message.content.filter(c => c.type === 'text'), [message]);
21
- return /*#__PURE__*/_jsxs(Box, {
22
- sx: message.role === 'user' ? styles.messageUser : styles.messageAssistant,
23
- children: [/*#__PURE__*/_jsx(ToolContextProvider, {
24
- message: message,
25
- children: /*#__PURE__*/_jsx(ToolContent, {})
26
- }), textContent.map((item, index) => /*#__PURE__*/_jsx(Box, {
27
- sx: styles.contentItem,
28
- children: /*#__PURE__*/_jsx(MarkdownContent, {
29
- content: item.value
24
+ return /*#__PURE__*/_jsxs(Stack, {
25
+ gap: 0.5,
26
+ sx: {
27
+ ...styles.options.base,
28
+ ...(retryable && styles.options.hover)
29
+ },
30
+ children: [/*#__PURE__*/_jsxs(Box, {
31
+ sx: message.role === 'user' ? styles.messageUser : styles.messageAssistant,
32
+ children: [/*#__PURE__*/_jsx(ToolContextProvider, {
33
+ message: message,
34
+ children: /*#__PURE__*/_jsx(ToolContent, {})
35
+ }), textContent.map((item, index) => /*#__PURE__*/_jsx(Box, {
36
+ sx: styles.contentItem,
37
+ children: /*#__PURE__*/_jsx(MarkdownContent, {
38
+ content: item.value
39
+ })
40
+ }, index))]
41
+ }), /*#__PURE__*/_jsx(Stack, {
42
+ className: "chat-options",
43
+ direction: "row",
44
+ justifyContent: "flex-end",
45
+ children: /*#__PURE__*/_jsx(Tooltip, {
46
+ title: "\uC7AC\uC2DC\uB3C4",
47
+ children: /*#__PURE__*/_jsx(IconButton, {
48
+ onClick: onRetry,
49
+ size: "small",
50
+ children: /*#__PURE__*/_jsx(ReplayRoundedIcon, {
51
+ fontSize: "small"
52
+ })
53
+ })
30
54
  })
31
- }, index))]
55
+ })]
32
56
  });
33
57
  };
34
58
  export default ChatMessage;
@@ -72,5 +96,20 @@ const styles = {
72
96
  '&:last-child': {
73
97
  marginBottom: 0
74
98
  }
99
+ },
100
+ options: {
101
+ base: {
102
+ '& .chat-options': {
103
+ opacity: 0,
104
+ pointerEvents: 'none',
105
+ transition: 'opacity 0.15s ease'
106
+ }
107
+ },
108
+ hover: {
109
+ '&:hover .chat-options': {
110
+ opacity: 1,
111
+ pointerEvents: 'auto'
112
+ }
113
+ }
75
114
  }
76
115
  };
@@ -1,22 +1,29 @@
1
- import React, { useEffect } from 'react';
1
+ import React, { useEffect, useMemo } from 'react';
2
2
  import ChatMessage from './ChatMessage';
3
3
  import LoadingIndicator from './LoadingIndicator';
4
+ import { useChatContext } from "./contexts/ChatContext";
4
5
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
6
  const ChatMessages = ({
6
7
  messages,
7
8
  isLoading,
8
9
  chatContainerRef
9
10
  }) => {
11
+ const {
12
+ retrySend
13
+ } = useChatContext();
10
14
  useEffect(() => {
11
15
  if (messages.length === 0) return;
12
16
  const message = messages[messages.length - 1];
13
17
  //if(message.role === 'assistant') console.log(message);
14
18
  }, [messages.length]);
19
+ const lastUserIdx = useMemo(() => messages.findLastIndex(m => m.role === 'user'), [messages.length]);
15
20
  return /*#__PURE__*/_jsxs("div", {
16
21
  style: styles.container,
17
22
  ref: chatContainerRef,
18
23
  children: [messages.map((message, index) => /*#__PURE__*/_jsx(ChatMessage, {
19
- message: message
24
+ message: message,
25
+ retryable: !isLoading && index === lastUserIdx,
26
+ onRetry: _ => retrySend(message.content[0].value)
20
27
  }, index)), isLoading && /*#__PURE__*/_jsx(LoadingIndicator, {})]
21
28
  });
22
29
  };
@@ -28,7 +35,6 @@ const styles = {
28
35
  padding: '10px',
29
36
  display: 'flex',
30
37
  flexDirection: 'column',
31
- gap: '1rem',
32
38
  background: '#fcfcfd'
33
39
  }
34
40
  };
@@ -20,8 +20,9 @@ export function ChatProvider({
20
20
  const [option, setOption] = useState();
21
21
  const options = useMemo(() => defaultOptions);
22
22
  const [tools, setTools] = useState([]);
23
+ const [pagePath, setPagePath] = useState();
23
24
  const chatContainerRef = useRef(null);
24
- const mainPageRef = useRef(null);
25
+ const pageRef = useRef(null);
25
26
  useEffect(() => {
26
27
  loadModels();
27
28
  // loadTools();
@@ -78,11 +79,11 @@ export function ChatProvider({
78
79
  return await response.json();
79
80
  }
80
81
  async function analyzePage(message) {
81
- if (!mainPageRef.current) throw new Error('No main page');
82
+ if (!pageRef.current) throw new Error('No main page');
82
83
  const {
83
84
  apiUrl
84
85
  } = getConfig();
85
- const html = mainPageRef.current?.innerHTML;
86
+ const html = pageRef.current?.innerHTML;
86
87
  const response = await fetch(`${apiUrl}/api/pages/analyze`, {
87
88
  method: 'POST',
88
89
  headers: {
@@ -92,7 +93,7 @@ export function ChatProvider({
92
93
  conversationId: conversationId,
93
94
  message: message,
94
95
  htmlContent: html,
95
- pageUrl: window.location.href,
96
+ pagePath: pagePath,
96
97
  provider: provider,
97
98
  model: model,
98
99
  forceRefresh: true
@@ -100,18 +101,20 @@ export function ChatProvider({
100
101
  });
101
102
  return await response.json();
102
103
  }
103
- const sendMessage = async () => {
104
- const message = inputValue.trim();
104
+ const sendMessage = async (messageToSend = null) => {
105
+ const message = messageToSend ?? inputValue.trim();
105
106
  if (!message) return;
106
- const userMessage = {
107
- role: 'user',
108
- content: [{
109
- type: 'text',
110
- value: message
111
- }]
112
- };
113
- setMessages(prev => [...prev, userMessage]);
114
- setInputValue('');
107
+ if (!messageToSend) {
108
+ const userMessage = {
109
+ role: 'user',
110
+ content: [{
111
+ type: 'text',
112
+ value: message
113
+ }]
114
+ };
115
+ setMessages(prev => [...prev, userMessage]);
116
+ setInputValue('');
117
+ }
115
118
  setIsLoading(true);
116
119
  try {
117
120
  let data = option === 'ANALYZE' ? await analyzePage(message) : await answer(message);
@@ -139,6 +142,10 @@ export function ChatProvider({
139
142
  setConversationId(null);
140
143
  setInputValue('');
141
144
  };
145
+ const retrySend = async retryMessage => {
146
+ setMessages(prev => prev.at(-1)?.role === 'assistant' ? prev.slice(0, -1) : prev);
147
+ await sendMessage(retryMessage);
148
+ };
142
149
  const value = {
143
150
  messages,
144
151
  inputValue,
@@ -153,9 +160,12 @@ export function ChatProvider({
153
160
  option,
154
161
  setOption,
155
162
  tools,
163
+ pagePath,
164
+ setPagePath,
156
165
  chatContainerRef,
157
- mainPageRef,
166
+ pageRef,
158
167
  sendMessage,
168
+ retrySend,
159
169
  clearChat,
160
170
  conversationId
161
171
  };
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { Box, Divider, MenuItem, Select } from "@mui/material";
3
3
  import { useChatContext } from "../contexts/ChatContext";
4
+ import typography from "@nethru/ui/base/styles/typography";
4
5
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
5
6
  export default function ModelSelect({
6
7
  ...props
@@ -26,7 +27,10 @@ export default function ModelSelect({
26
27
  onChange: onChange,
27
28
  disableUnderline: true,
28
29
  sx: styles.select,
29
- renderValue: value => findModel(providers, value)?.name,
30
+ renderValue: value => /*#__PURE__*/_jsx(Box, {
31
+ sx: styles.value,
32
+ children: findModel(providers, value)?.name
33
+ }),
30
34
  ...props,
31
35
  children: makeOptions(providers)
32
36
  })
@@ -41,10 +45,10 @@ function makeOptions(providers) {
41
45
  value: model.id,
42
46
  children: /*#__PURE__*/_jsxs(Box, {
43
47
  children: [/*#__PURE__*/_jsx(Box, {
44
- sx: styles.name,
48
+ sx: styles.option.name,
45
49
  children: model.name
46
50
  }), /*#__PURE__*/_jsx(Box, {
47
- sx: styles.desc,
51
+ sx: styles.option.desc,
48
52
  children: model.description
49
53
  })]
50
54
  })
@@ -72,12 +76,17 @@ const styles = {
72
76
  color: '#666',
73
77
  fontSize: '11px'
74
78
  },
75
- name: {
76
- fontWeight: 400,
77
- fontSize: '12px'
79
+ value: {
80
+ fontFamily: typography.fontFamily
78
81
  },
79
- desc: {
80
- fontSize: '11px',
81
- fontWeight: 200
82
+ option: {
83
+ name: {
84
+ fontWeight: 400,
85
+ fontSize: '12px'
86
+ },
87
+ desc: {
88
+ fontSize: '11px',
89
+ fontWeight: 200
90
+ }
82
91
  }
83
92
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nethru/kit",
3
- "version": "1.1.15",
3
+ "version": "1.1.18",
4
4
  "description": "A React component library by Nethru",
5
5
  "main": "dist/index.js",
6
6
  "homepage": ".",
File without changes