@nethru/kit 1.0.7 → 1.1.1

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,168 @@
1
+ import React, { useMemo } from 'react';
2
+ import Highcharts from 'highcharts';
3
+ import HighchartsReactModule from 'highcharts-react-official';
4
+ import 'highcharts/modules/pattern-fill.js';
5
+
6
+ // Handle both ES module and CommonJS exports
7
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
+ const HighchartsReact = HighchartsReactModule.default || HighchartsReactModule;
9
+ const PieChart = ({
10
+ data,
11
+ width = 230,
12
+ height = 180,
13
+ size = '60%',
14
+ colors = ['#2652a8', '#b47813', '#aa1a32']
15
+ }) => {
16
+ const noData = data.metrics.some(m => m.transparent);
17
+ const options = useMemo(() => {
18
+ const chartData = data.metrics.filter(metric => metric.transparent || metric.value > 0).map((metric, index) => ({
19
+ name: metric.label,
20
+ y: metric.value,
21
+ color: metric.transparent ? checkerboardPattern() : colors[index % colors.length]
22
+ }));
23
+ return {
24
+ chart: {
25
+ type: 'pie',
26
+ backgroundColor: 'transparent',
27
+ width: width,
28
+ height: height,
29
+ style: {
30
+ fontFamily: fontFamily
31
+ }
32
+ },
33
+ title: {
34
+ text: undefined
35
+ },
36
+ tooltip: {
37
+ pointFormat: '<span><b>{point.y}</b> ({point.percentage:.1f}%)</span>',
38
+ style: {
39
+ fontFamily: fontFamily,
40
+ fontSize: '13px'
41
+ },
42
+ useHTML: true
43
+ },
44
+ plotOptions: {
45
+ pie: {
46
+ size: size,
47
+ allowPointSelect: true,
48
+ cursor: 'pointer',
49
+ dataLabels: {
50
+ enabled: true,
51
+ format: '{point.name}<br><b>{point.y}</b> ({point.percentage:.1f}%)',
52
+ distance: 20,
53
+ style: {
54
+ fontSize: '13px',
55
+ fontWeight: '100',
56
+ textOutline: 'none',
57
+ fontFamily: fontFamily
58
+ },
59
+ connectorColor: '#666',
60
+ connectorWidth: 1,
61
+ overflow: 'allow',
62
+ crop: false
63
+ },
64
+ showInLegend: false
65
+ }
66
+ },
67
+ series: [{
68
+ type: 'pie',
69
+ name: '',
70
+ colorByPoint: true,
71
+ data: chartData
72
+ }],
73
+ credits: {
74
+ enabled: false
75
+ },
76
+ accessibility: {
77
+ enabled: false
78
+ }
79
+ };
80
+ }, [data, width, height, size, colors]);
81
+ return /*#__PURE__*/_jsxs("div", {
82
+ style: styles.chartWrapper,
83
+ children: [/*#__PURE__*/_jsxs("div", {
84
+ style: {
85
+ ...styles.chartContainer,
86
+ width,
87
+ height,
88
+ position: 'relative'
89
+ },
90
+ children: [noData && /*#__PURE__*/_jsx(CheckerboardBackground, {
91
+ width: width,
92
+ height: height
93
+ }), /*#__PURE__*/_jsx("div", {
94
+ style: {
95
+ position: 'relative',
96
+ zIndex: 1
97
+ },
98
+ children: /*#__PURE__*/_jsx(HighchartsReact, {
99
+ highcharts: Highcharts,
100
+ options: options
101
+ })
102
+ })]
103
+ }), /*#__PURE__*/_jsx("div", {
104
+ style: styles.agentName,
105
+ children: data.name
106
+ })]
107
+ });
108
+ };
109
+ export default PieChart;
110
+ const fontFamily = 'Pretendard, -apple-system, BlinkMacSystemFont, sans-serif';
111
+ const styles = {
112
+ chartWrapper: {
113
+ background: 'transparent',
114
+ textAlign: 'center',
115
+ display: 'flex',
116
+ flexDirection: 'column',
117
+ justifyContent: 'center',
118
+ alignItems: 'center'
119
+ },
120
+ chartContainer: {
121
+ display: 'flex',
122
+ alignItems: 'center',
123
+ justifyContent: 'center'
124
+ },
125
+ agentName: {
126
+ fontSize: '12px',
127
+ fontWeight: '100',
128
+ marginTop: '-30px',
129
+ color: '#333'
130
+ }
131
+ };
132
+ const CheckerboardBackground = ({
133
+ width,
134
+ height
135
+ }) => {
136
+ const circleSize = Math.min(width, height) * 0.47;
137
+ return /*#__PURE__*/_jsx("div", {
138
+ style: {
139
+ position: 'absolute',
140
+ width: circleSize,
141
+ height: circleSize,
142
+ borderRadius: '50%',
143
+ backgroundImage: `
144
+ linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
145
+ linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
146
+ linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
147
+ linear-gradient(-45deg, transparent 75%, #e0e0e0 75%)
148
+ `,
149
+ backgroundSize: '10px 10px',
150
+ backgroundPosition: '0 0, 0 5px, 5px -5px, -5px 0px',
151
+ backgroundColor: '#ffffff',
152
+ zIndex: 0
153
+ }
154
+ });
155
+ };
156
+ const checkerboardPattern = () => ({
157
+ pattern: {
158
+ path: {
159
+ d: 'M 0 0 L 5 5 M 4.5 -0.5 L 5.5 0.5 M -0.5 4.5 L 0.5 5.5',
160
+ stroke: '#d0d0d0',
161
+ strokeWidth: 1
162
+ },
163
+ width: 5,
164
+ height: 5,
165
+ backgroundColor: '#ffffff',
166
+ opacity: 1
167
+ }
168
+ });
@@ -0,0 +1,52 @@
1
+ import React, { useMemo } from "react";
2
+ import TrendChart from "./TrendChart";
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ export default function StackedAreaTrendChart({
5
+ metrics,
6
+ records,
7
+ isDaily = false,
8
+ xPlotIndex,
9
+ xAxisTickInterval,
10
+ yAxisLabelEnabled = false,
11
+ legendLimit = 3,
12
+ colors = defaultColors,
13
+ ...props
14
+ }) {
15
+ const _metrics = useMemo(() => {
16
+ return metrics.map(m => ({
17
+ metric: m.id,
18
+ chartType: 'area'
19
+ }));
20
+ }, [metrics]);
21
+ const metas = useMemo(() => {
22
+ const result = {
23
+ time: {
24
+ name: "일시",
25
+ type: "DATE"
26
+ }
27
+ };
28
+ metrics.forEach(m => {
29
+ result[m.id] = {
30
+ name: m.name,
31
+ type: "NUMBER"
32
+ };
33
+ });
34
+ return result;
35
+ }, [metrics]);
36
+ return /*#__PURE__*/_jsx(TrendChart, {
37
+ dimension: "time",
38
+ stacking: "normal",
39
+ metrics: _metrics,
40
+ metas: metas,
41
+ records: records,
42
+ isRealtime: xPlotIndex !== undefined,
43
+ isDaily: isDaily,
44
+ xPlotIndex: xPlotIndex,
45
+ xAxisTickInterval: xAxisTickInterval,
46
+ yAxisLabelEnabled: yAxisLabelEnabled,
47
+ legendLimit: legendLimit,
48
+ colors: colors,
49
+ ...props
50
+ });
51
+ }
52
+ const defaultColors = [["rgba(54,115,237,0.8)", "#3673ed"], ["rgba(149,77,241,0.8)", "#954df1"], ["#4dc391", "#20b476"], ["#fbba3d", "#faa90c"], ["#f2426d", "#ef1348"]];
@@ -0,0 +1,30 @@
1
+ import React, { useMemo } from "react";
2
+ import Chart from "./Chart";
3
+ import * as ChartColors from "./ChartColors";
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ export default function TrendChart({
6
+ colorIndex = 0,
7
+ legendColors,
8
+ isDaily,
9
+ isRealtime,
10
+ ...props
11
+ }) {
12
+ const colors = useMemo(() => {
13
+ return ChartColors.all[colorIndex % ChartColors.all.length];
14
+ }, [colorIndex]);
15
+ const tooltip = useMemo(() => ({
16
+ dateFormat: isRealtime ? '%H:%M' : '%Y-%m-%d',
17
+ headerTime: !isDaily && !isRealtime,
18
+ outside: props.tooltipOutSide,
19
+ legendColors
20
+ }), [isRealtime, isDaily, props.tooltipOutSide, legendColors]);
21
+ return /*#__PURE__*/_jsx(Chart, {
22
+ type: "areaspline",
23
+ categorize: true,
24
+ xAxisType: "datetime",
25
+ xAxisLabelFormat: isDaily ? '%m-%d' : '%H:%M',
26
+ colors: colors,
27
+ tooltip: tooltip,
28
+ ...props
29
+ });
30
+ }
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { Stack } from "@mui/material";
3
+ import ChatMessages from './ChatMessages';
4
+ import ChatInput from './ChatInput';
5
+ import useChatApi from './hooks/useChatApi';
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ function AiChat({
8
+ defaultMessages,
9
+ sx
10
+ }) {
11
+ const {
12
+ messages,
13
+ inputValue,
14
+ setInputValue,
15
+ isLoading,
16
+ chatContainerRef,
17
+ sendMessage
18
+ } = useChatApi(defaultMessages);
19
+ return /*#__PURE__*/_jsxs(Stack, {
20
+ sx: {
21
+ ...styles.container,
22
+ ...sx
23
+ },
24
+ children: [/*#__PURE__*/_jsx(ChatMessages, {
25
+ messages: messages,
26
+ isLoading: isLoading,
27
+ chatContainerRef: chatContainerRef
28
+ }), /*#__PURE__*/_jsx(ChatInput, {
29
+ inputValue: inputValue,
30
+ setInputValue: setInputValue,
31
+ onSend: sendMessage,
32
+ isLoading: isLoading
33
+ })]
34
+ });
35
+ }
36
+ export default AiChat;
37
+ const styles = {
38
+ container: {
39
+ width: '100%',
40
+ height: '90vh',
41
+ background: 'white',
42
+ display: 'flex',
43
+ flexDirection: 'column',
44
+ overflow: 'hidden'
45
+ }
46
+ };
@@ -0,0 +1,112 @@
1
+ import React from 'react';
2
+ import { Box, FormControl, IconButton, MenuItem, Select, Stack, TextField } from '@mui/material';
3
+ import { Send as SendIcon } from '@mui/icons-material';
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ const ChatInput = ({
6
+ inputValue,
7
+ setInputValue,
8
+ onSend,
9
+ isLoading
10
+ }) => {
11
+ const [model, setModel] = React.useState('GPT_4_1_MINI');
12
+ const handleKeyPress = e => {
13
+ if (e.key === 'Enter' && !e.shiftKey) {
14
+ e.preventDefault();
15
+ onSend();
16
+ }
17
+ };
18
+ return /*#__PURE__*/_jsx(Box, {
19
+ sx: styles.container,
20
+ children: /*#__PURE__*/_jsxs(Stack, {
21
+ sx: styles.textArea,
22
+ children: [/*#__PURE__*/_jsx(TextField, {
23
+ fullWidth: true,
24
+ multiline: true,
25
+ placeholder: "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD558\uC138\uC694...",
26
+ value: inputValue,
27
+ onChange: e => setInputValue(e.target.value),
28
+ onKeyUp: handleKeyPress,
29
+ disabled: isLoading,
30
+ variant: "standard",
31
+ slotProps: {
32
+ input: {
33
+ disableUnderline: true,
34
+ sx: styles.textField
35
+ }
36
+ }
37
+ }), /*#__PURE__*/_jsxs(Stack, {
38
+ direction: "row",
39
+ gap: 2,
40
+ justifyContent: "flex-end",
41
+ alignItems: "center",
42
+ children: [/*#__PURE__*/_jsx(FormControl, {
43
+ variant: "standard",
44
+ children: /*#__PURE__*/_jsxs(Select, {
45
+ variant: "standard",
46
+ value: model,
47
+ onChange: e => setModel(e.target.value),
48
+ disableUnderline: true,
49
+ sx: {
50
+ fontSize: '13px'
51
+ },
52
+ children: [/*#__PURE__*/_jsx(MenuItem, {
53
+ value: "GPT_4_1_MINI",
54
+ children: "GPT-4.1 Mini"
55
+ }), /*#__PURE__*/_jsx(MenuItem, {
56
+ value: "CLAUDE_3",
57
+ children: "Claude 3"
58
+ })]
59
+ })
60
+ }), /*#__PURE__*/_jsx(IconButton, {
61
+ onClick: onSend,
62
+ disabled: isLoading || !inputValue.trim(),
63
+ sx: styles.iconButton,
64
+ children: /*#__PURE__*/_jsx(SendIcon, {
65
+ sx: {
66
+ fontSize: 18
67
+ }
68
+ })
69
+ })]
70
+ })]
71
+ })
72
+ });
73
+ };
74
+ export default ChatInput;
75
+ const styles = {
76
+ container: {
77
+ background: '#f5f5f5',
78
+ padding: '20px',
79
+ borderTop: '1px solid #e5e7eb'
80
+ },
81
+ textArea: {
82
+ border: '1px solid #dfdfdf',
83
+ background: '#fff',
84
+ borderRadius: '12px',
85
+ padding: '4px 12px',
86
+ transition: 'all 0.2s ease-in-out'
87
+ },
88
+ textField: {
89
+ fontSize: '14px',
90
+ padding: '8px 4px',
91
+ lineHeight: '1.4',
92
+ transition: 'all 0.2s ease-in-out',
93
+ '& textarea': {
94
+ transition: 'height 0.2s ease-in-out !important'
95
+ }
96
+ },
97
+ iconButton: {
98
+ background: '#000',
99
+ color: 'white',
100
+ width: 30,
101
+ height: 30,
102
+ paddingLeft: '12px',
103
+ transition: 'background-color 0.3s ease',
104
+ '&:hover': {
105
+ background: '#333'
106
+ },
107
+ '&:disabled': {
108
+ background: '#e0e0e0',
109
+ color: '#9e9e9e'
110
+ }
111
+ }
112
+ };
@@ -0,0 +1,76 @@
1
+ import React, { useMemo } from 'react';
2
+ import { Box, keyframes } from '@mui/material';
3
+ import MarkdownContent from "./content/MarkdownContent";
4
+ import ToolContent from "./content/ToolContent";
5
+ import { ToolContextProvider } from "./contexts/ToolContext";
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ const slideIn = keyframes`
8
+ from {
9
+ opacity: 0;
10
+ transform: translateY(10px);
11
+ }
12
+ to {
13
+ opacity: 1;
14
+ transform: translateY(0);
15
+ }
16
+ `;
17
+ const ChatMessage = ({
18
+ message
19
+ }) => {
20
+ 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
30
+ })
31
+ }, index))]
32
+ });
33
+ };
34
+ export default ChatMessage;
35
+ const styles = {
36
+ messageUser: {
37
+ maxWidth: '75%',
38
+ padding: '5px 10px',
39
+ borderRadius: '12px',
40
+ wordWrap: 'break-word',
41
+ fontSize: '13px',
42
+ animation: `${slideIn} 0.3s ease`,
43
+ color: '#333',
44
+ background: '#ecedee',
45
+ alignSelf: 'flex-end',
46
+ marginLeft: 'auto',
47
+ '& .content-tool': {
48
+ background: 'rgba(255, 255, 255, 0.2)',
49
+ borderLeftColor: 'rgba(255, 255, 255, 0.5)'
50
+ },
51
+ '& .tool-header': {
52
+ color: 'rgba(255, 255, 255, 0.95)'
53
+ },
54
+ '& .tool-result': {
55
+ background: 'rgba(255, 255, 255, 0.15)',
56
+ color: 'rgba(255, 255, 255, 0.9)'
57
+ }
58
+ },
59
+ messageAssistant: {
60
+ width: '100%',
61
+ maxWidth: '100%',
62
+ padding: '1rem 1.25rem',
63
+ borderRadius: '1.25rem',
64
+ wordWrap: 'break-word',
65
+ fontSize: '13px',
66
+ animation: `${slideIn} 0.3s ease`,
67
+ color: '#333',
68
+ alignSelf: 'flex-start'
69
+ },
70
+ contentItem: {
71
+ marginBottom: '0.5rem',
72
+ '&:last-child': {
73
+ marginBottom: 0
74
+ }
75
+ }
76
+ };
@@ -0,0 +1,34 @@
1
+ import React, { useEffect } from 'react';
2
+ import ChatMessage from './ChatMessage';
3
+ import LoadingIndicator from './LoadingIndicator';
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ const ChatMessages = ({
6
+ messages,
7
+ isLoading,
8
+ chatContainerRef
9
+ }) => {
10
+ useEffect(() => {
11
+ if (messages.length === 0) return;
12
+ const message = messages[messages.length - 1];
13
+ if (message.role === 'assistant') console.log(message);
14
+ }, [messages.length]);
15
+ return /*#__PURE__*/_jsxs("div", {
16
+ style: styles.container,
17
+ ref: chatContainerRef,
18
+ children: [messages.map((message, index) => /*#__PURE__*/_jsx(ChatMessage, {
19
+ message: message
20
+ }, index)), isLoading && /*#__PURE__*/_jsx(LoadingIndicator, {})]
21
+ });
22
+ };
23
+ export default ChatMessages;
24
+ const styles = {
25
+ container: {
26
+ flex: 1,
27
+ overflowY: 'auto',
28
+ padding: '1.5rem',
29
+ display: 'flex',
30
+ flexDirection: 'column',
31
+ gap: '1rem',
32
+ background: '#fcfcfd'
33
+ }
34
+ };
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { Box, keyframes } from '@mui/material';
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ const bounce = keyframes`
5
+ 0%, 60%, 100% {
6
+ transform: translateY(0);
7
+ }
8
+ 30% {
9
+ transform: translateY(-10px);
10
+ }
11
+ `;
12
+ const LoadingIndicator = () => {
13
+ return /*#__PURE__*/_jsxs(Box, {
14
+ sx: styles.typing,
15
+ children: [/*#__PURE__*/_jsx(Box, {
16
+ component: "span",
17
+ sx: styles.dot
18
+ }), /*#__PURE__*/_jsx(Box, {
19
+ component: "span",
20
+ sx: {
21
+ ...styles.dot,
22
+ animationDelay: '0.2s'
23
+ }
24
+ }), /*#__PURE__*/_jsx(Box, {
25
+ component: "span",
26
+ sx: {
27
+ ...styles.dot,
28
+ animationDelay: '0.4s'
29
+ }
30
+ })]
31
+ });
32
+ };
33
+ export default LoadingIndicator;
34
+ const styles = {
35
+ typing: {
36
+ display: 'inline-block',
37
+ padding: '1rem 1.25rem'
38
+ },
39
+ dot: {
40
+ display: 'inline-block',
41
+ width: '6px',
42
+ height: '6px',
43
+ borderRadius: '50%',
44
+ background: '#666',
45
+ margin: '0 2px',
46
+ animation: `${bounce} 1.4s infinite`
47
+ }
48
+ };