@nethru/kit 1.1.13 → 1.1.15

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.
@@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
2
2
  import { Stack } from "@mui/material";
3
3
  import { useChatContext } from './contexts/ChatContext';
4
4
  import ChatMessages from './ChatMessages';
5
- import ChatInput from './ChatInput';
5
+ import ChatInput from './input/ChatInput';
6
6
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  function AiChat({
8
8
  mainPageRef,
@@ -1,6 +1,7 @@
1
- import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
1
+ import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { getConfig } from "../../../js/config";
3
- import { isPageAnalysisIntent } from "../../../js/promptIntent";
3
+ import AutoStoriesIcon from "@mui/icons-material/AutoStories";
4
+ import WysiwygIcon from "@mui/icons-material/Wysiwyg";
4
5
  import { jsx as _jsx } from "react/jsx-runtime";
5
6
  const ChatContext = /*#__PURE__*/createContext();
6
7
  export function ChatProvider({
@@ -16,6 +17,8 @@ export function ChatProvider({
16
17
  const [provider, setProvider] = useState(defaultProvider);
17
18
  const [providers, setProviders] = useState([]);
18
19
  const [model, setModel] = useState(defaultModel);
20
+ const [option, setOption] = useState();
21
+ const options = useMemo(() => defaultOptions);
19
22
  const [tools, setTools] = useState([]);
20
23
  const chatContainerRef = useRef(null);
21
24
  const mainPageRef = useRef(null);
@@ -68,7 +71,8 @@ export function ChatProvider({
68
71
  conversationId: conversationId,
69
72
  message: message,
70
73
  provider: provider,
71
- model: model
74
+ model: model,
75
+ option: option
72
76
  })
73
77
  });
74
78
  return await response.json();
@@ -110,7 +114,7 @@ export function ChatProvider({
110
114
  setInputValue('');
111
115
  setIsLoading(true);
112
116
  try {
113
- let data = isPageAnalysisIntent(message) ? await analyzePage(message) : await answer(message);
117
+ let data = option === 'ANALYZE' ? await analyzePage(message) : await answer(message);
114
118
  setConversationId(data.conversationId);
115
119
  setMessages(prev => [...prev, {
116
120
  role: data.role,
@@ -145,6 +149,9 @@ export function ChatProvider({
145
149
  setProvider,
146
150
  model,
147
151
  setModel,
152
+ options,
153
+ option,
154
+ setOption,
148
155
  tools,
149
156
  chatContainerRef,
150
157
  mainPageRef,
@@ -164,4 +171,17 @@ export function useChatContext() {
164
171
  }
165
172
  return context;
166
173
  }
167
- export default ChatContext;
174
+ export default ChatContext;
175
+ const defaultOptions = [{
176
+ value: 'ANALYZE',
177
+ label: '페이지 요약 및 분석',
178
+ icon: /*#__PURE__*/_jsx(WysiwygIcon, {
179
+ fontSize: "small"
180
+ })
181
+ }, {
182
+ value: 'RAG',
183
+ label: '지식 베이스에서 문의',
184
+ icon: /*#__PURE__*/_jsx(AutoStoriesIcon, {
185
+ fontSize: "small"
186
+ })
187
+ }];
@@ -1,8 +1,9 @@
1
- import React, { useRef, useEffect, useState } from 'react';
2
- import { Box, FormControl, IconButton, Stack, TextField } from '@mui/material';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { Box, IconButton, Stack, TextField } from '@mui/material';
3
3
  import { Send as SendIcon } from '@mui/icons-material';
4
- import { useChatContext } from "./contexts/ChatContext";
4
+ import { useChatContext } from "../contexts/ChatContext";
5
5
  import ModelSelect from "./ModelSelect";
6
+ import Options from "./Options";
6
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
8
  const ChatInput = () => {
8
9
  const inputRef = useRef(null);
@@ -11,7 +12,10 @@ const ChatInput = () => {
11
12
  inputValue,
12
13
  setInputValue,
13
14
  isLoading,
14
- sendMessage
15
+ sendMessage,
16
+ option,
17
+ setOption,
18
+ options
15
19
  } = useChatContext();
16
20
  const handleKeyPress = e => {
17
21
  if (e.key === 'Enter' && !e.shiftKey) {
@@ -21,7 +25,7 @@ const ChatInput = () => {
21
25
  };
22
26
  useEffect(() => {
23
27
  if (!isLoading && !selectOpen && inputRef.current) inputRef.current.focus();
24
- }, [isLoading, selectOpen]);
28
+ }, [isLoading, selectOpen, option]);
25
29
  return /*#__PURE__*/_jsx(Box, {
26
30
  sx: styles.container,
27
31
  children: /*#__PURE__*/_jsxs(Stack, {
@@ -45,15 +49,21 @@ const ChatInput = () => {
45
49
  }), /*#__PURE__*/_jsxs(Stack, {
46
50
  direction: "row",
47
51
  gap: 2,
48
- justifyContent: "flex-end",
52
+ justifyContent: "space-between",
49
53
  alignItems: "center",
50
- children: [/*#__PURE__*/_jsx(FormControl, {
51
- variant: "standard",
52
- children: /*#__PURE__*/_jsx(ModelSelect, {
54
+ children: [/*#__PURE__*/_jsxs(Stack, {
55
+ direction: "row",
56
+ alignItems: "center",
57
+ gap: 2,
58
+ children: [/*#__PURE__*/_jsx(Options, {
59
+ options: options,
60
+ option: option,
61
+ setOption: setOption
62
+ }), /*#__PURE__*/_jsx(ModelSelect, {
53
63
  open: selectOpen,
54
64
  onOpen: () => setSelectOpen(true),
55
65
  onClose: () => setSelectOpen(false)
56
- })
66
+ })]
57
67
  }), /*#__PURE__*/_jsx(IconButton, {
58
68
  onClick: sendMessage,
59
69
  disabled: isLoading || !inputValue.trim(),
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { Box, Divider, MenuItem, Select } from "@mui/material";
3
- import { useChatContext } from "./contexts/ChatContext";
3
+ import { useChatContext } from "../contexts/ChatContext";
4
4
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
5
5
  export default function ModelSelect({
6
6
  ...props
@@ -70,12 +70,11 @@ function findModel(providers, value) {
70
70
  const styles = {
71
71
  select: {
72
72
  color: '#666',
73
- fontSize: '13px',
74
- fontWeight: 400
73
+ fontSize: '11px'
75
74
  },
76
75
  name: {
77
76
  fontWeight: 400,
78
- fontSize: '13px'
77
+ fontSize: '12px'
79
78
  },
80
79
  desc: {
81
80
  fontSize: '11px',
@@ -0,0 +1,72 @@
1
+ import React from "react";
2
+ import { ToggleButton, toggleButtonClasses, ToggleButtonGroup, toggleButtonGroupClasses, Tooltip, Typography } from "@mui/material";
3
+ import { styled } from '@mui/material/styles';
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({
6
+ theme
7
+ }) => ({
8
+ gap: '5px',
9
+ [`& .${toggleButtonGroupClasses.firstButton}, & .${toggleButtonGroupClasses.middleButton}`]: {
10
+ borderTopRightRadius: '6px',
11
+ borderBottomRightRadius: '6px'
12
+ },
13
+ [`& .${toggleButtonGroupClasses.lastButton}, & .${toggleButtonGroupClasses.middleButton}`]: {
14
+ borderTopLeftRadius: '6px',
15
+ borderBottomLeftRadius: '6px'
16
+ },
17
+ [`& .${toggleButtonGroupClasses.lastButton}.${toggleButtonClasses.disabled}, & .${toggleButtonGroupClasses.middleButton}.${toggleButtonClasses.disabled}`]: {}
18
+ }));
19
+ export default function Options({
20
+ options = [],
21
+ option,
22
+ setOption,
23
+ ...props
24
+ }) {
25
+ return /*#__PURE__*/_jsx(StyledToggleButtonGroup, {
26
+ exclusive: true,
27
+ value: option,
28
+ onChange: (_, value) => setOption(value),
29
+ children: options.map(o => /*#__PURE__*/_jsx(Tooltip, {
30
+ title: option === o.value ? "" : o.label,
31
+ slotProps: {
32
+ tooltip: {
33
+ sx: styles.tooltip
34
+ }
35
+ },
36
+ children: /*#__PURE__*/_jsxs(ToggleButton, {
37
+ value: o.value,
38
+ size: "small",
39
+ sx: styles.button(option === o.value),
40
+ ...props,
41
+ children: [o.icon, option === o.value && /*#__PURE__*/_jsx(Typography, {
42
+ sx: styles.text,
43
+ children: o.label
44
+ })]
45
+ })
46
+ }, o.value))
47
+ });
48
+ }
49
+ const styles = {
50
+ button: selected => ({
51
+ border: 'none',
52
+ width: selected ? 'auto' : '25px',
53
+ height: '25px',
54
+ minWidth: 0,
55
+ padding: selected ? '0 8px' : '0',
56
+ '&.Mui-selected': {
57
+ backgroundColor: '#333',
58
+ color: 'white',
59
+ '&:hover': {
60
+ backgroundColor: 'black'
61
+ }
62
+ }
63
+ }),
64
+ text: {
65
+ ml: 1,
66
+ fontSize: '11px',
67
+ whiteSpace: 'nowrap'
68
+ },
69
+ tooltip: {
70
+ fontSize: '10px'
71
+ }
72
+ };
@@ -1,29 +0,0 @@
1
- export function isPageAnalysisIntent(userPrompt) {
2
- if (!userPrompt || userPrompt.trim() === "") return false;
3
- const analysisKeywords = ["요약", "정리", "분석", "페이지", "화면", "글", "기사", "문서", "내용", "포스트", "블로그", "뭐야", "뭔가", "무엇", "어떤", "핵심", "주요", "포인트", "요점", "이", "현재", "지금", "여기", "summarize", "summary", "analyze", "analysis", "explain", "this page", "current page", "this article", "this post"];
4
- const excludeKeywords = ["http://", "https://", "www.", "날씨", "weather", "뉴스", "news", "코드", "code", "프로그램", "program"];
5
- const prompt = userPrompt.toLowerCase();
6
-
7
- // 제외 키워드 포함 시, "이 페이지" 패턴만 예외 허용
8
- if (excludeKeywords.some(k => prompt.includes(k))) {
9
- if (!(prompt.includes("이") && prompt.includes("페이지"))) {
10
- return false;
11
- }
12
- }
13
- const matchedKeywords = analysisKeywords.filter(k => prompt.includes(k));
14
-
15
- // 최소 2개 이상의 키워드 매칭
16
- if (matchedKeywords.length >= 2) return true;
17
-
18
- // 단일 키워드지만 강한 패턴
19
- // const strongPatterns = [
20
- // "요약해", "정리해", "분석해",
21
- // "요약 해", "정리 해", "분석 해",
22
- // "페이지 요약", "페이지 정리", "페이지 분석",
23
- // "summarize this", "analyze this", "explain this"
24
- // ];
25
- //
26
- // if(strongPatterns.some(p => prompt.includes(p))) return true;
27
-
28
- return false;
29
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nethru/kit",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
4
4
  "description": "A React component library by Nethru",
5
5
  "main": "dist/index.js",
6
6
  "homepage": ".",