@stack-spot/ai-chat-widget 1.20.2 → 1.21.1-beta.0
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.
- package/CHANGELOG.md +14 -0
- package/dist/app-metadata.json +5 -5
- package/dist/components/ProgressBar.d.ts +5 -1
- package/dist/components/ProgressBar.d.ts.map +1 -1
- package/dist/components/ProgressBar.js +8 -8
- package/dist/components/ProgressBar.js.map +1 -1
- package/dist/layout.css +2 -1
- package/dist/views/Chat/ChatMessage.d.ts +2 -2
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +12 -7
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/Chat/styled.d.ts.map +1 -1
- package/dist/views/Chat/styled.js +9 -4
- package/dist/views/Chat/styled.js.map +1 -1
- package/dist/views/MessageInput/ButtonBar.d.ts +17 -0
- package/dist/views/MessageInput/ButtonBar.d.ts.map +1 -0
- package/dist/views/MessageInput/ButtonBar.js +16 -0
- package/dist/views/MessageInput/ButtonBar.js.map +1 -0
- package/dist/views/MessageInput/InfoBar.d.ts.map +1 -1
- package/dist/views/MessageInput/InfoBar.js +2 -2
- package/dist/views/MessageInput/InfoBar.js.map +1 -1
- package/dist/views/MessageInput/SelectContent.d.ts +2 -0
- package/dist/views/MessageInput/SelectContent.d.ts.map +1 -0
- package/dist/views/MessageInput/SelectContent.js +60 -0
- package/dist/views/MessageInput/SelectContent.js.map +1 -0
- package/dist/views/MessageInput/dictionary.js +2 -2
- package/dist/views/MessageInput/dictionary.js.map +1 -1
- package/dist/views/MessageInput/index.d.ts.map +1 -1
- package/dist/views/MessageInput/index.js +4 -8
- package/dist/views/MessageInput/index.js.map +1 -1
- package/dist/views/MessageInput/styled.d.ts +6 -1
- package/dist/views/MessageInput/styled.d.ts.map +1 -1
- package/dist/views/MessageInput/styled.js +55 -45
- package/dist/views/MessageInput/styled.js.map +1 -1
- package/package.json +2 -2
- package/src/app-metadata.json +5 -5
- package/src/components/ProgressBar.tsx +15 -8
- package/src/layout.css +2 -1
- package/src/views/Chat/ChatMessage.tsx +71 -18
- package/src/views/Chat/styled.ts +9 -4
- package/src/views/MessageInput/ButtonBar.tsx +55 -0
- package/src/views/MessageInput/InfoBar.tsx +6 -2
- package/src/views/MessageInput/SelectContent.tsx +88 -0
- package/src/views/MessageInput/dictionary.ts +2 -2
- package/src/views/MessageInput/index.tsx +8 -18
- package/src/views/MessageInput/styled.ts +57 -46
- package/dist/views/MessageInput/ButtonGroup.d.ts +0 -29
- package/dist/views/MessageInput/ButtonGroup.d.ts.map +0 -1
- package/dist/views/MessageInput/ButtonGroup.js +0 -34
- package/dist/views/MessageInput/ButtonGroup.js.map +0 -1
- package/src/views/MessageInput/ButtonGroup.tsx +0 -109
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { listToClass } from '@stack-spot/portal-theme';
|
|
2
|
+
import { listToClass, theme } from '@stack-spot/portal-theme';
|
|
3
3
|
import { interpolate } from '@stack-spot/portal-translate';
|
|
4
4
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
5
5
|
import { AdaptiveTextArea } from '../../components/AdaptiveTextArea.js';
|
|
@@ -10,7 +10,7 @@ import { ChatEntry } from '../../state/ChatEntry.js';
|
|
|
10
10
|
import { checkIsTrial } from '../../utils/check-is-trial.js';
|
|
11
11
|
import { AgentSelector } from './AgentSelector.js';
|
|
12
12
|
import { ButtonAgent } from './ButtonAgent.js';
|
|
13
|
-
import {
|
|
13
|
+
import { ButtonBar } from './ButtonBar.js';
|
|
14
14
|
import { useUserEntryHistoryShortcut } from './chat-entry-history.js';
|
|
15
15
|
import { useMessageInputDictionary } from './dictionary.js';
|
|
16
16
|
import { InfoBar } from './InfoBar.js';
|
|
@@ -24,7 +24,7 @@ import { MAX_INPUT_HEIGHT, MessageInputBox, MIN_INPUT_HEIGHT } from './styled.js
|
|
|
24
24
|
export const MessageInput = () => {
|
|
25
25
|
const t = useMessageInputDictionary();
|
|
26
26
|
const [focused, setFocused] = useState(false);
|
|
27
|
-
const [
|
|
27
|
+
const [, setExpanded] = useState(true);
|
|
28
28
|
const expansionLocked = useRef(false);
|
|
29
29
|
const chat = useCurrentChat();
|
|
30
30
|
const isLoading = useCurrentChatState('isLoading') ?? false;
|
|
@@ -43,7 +43,6 @@ export const MessageInput = () => {
|
|
|
43
43
|
const prompt = code && !quickCommandRegex.test(message) ? `${message}\n\`\`\`${language}\n${code}\n\`\`\`` : message;
|
|
44
44
|
chat.pushMessage(ChatEntry.createUserEntry(prompt, true));
|
|
45
45
|
chat.set('nextMessage', '');
|
|
46
|
-
setFocused(false);
|
|
47
46
|
}, [chat]);
|
|
48
47
|
const onKeyDown = useCallback((event) => {
|
|
49
48
|
if (!event.shiftKey && event.key === 'Enter') {
|
|
@@ -56,9 +55,6 @@ export const MessageInput = () => {
|
|
|
56
55
|
if (!isLoading)
|
|
57
56
|
textAreaRef.current?.focus();
|
|
58
57
|
}, [isLoading]);
|
|
59
|
-
return (_jsxs(MessageInputBox, { "aria-busy": isLoading, className: "message-input",
|
|
60
|
-
setExpanded(value);
|
|
61
|
-
expansionLocked.current = expanded;
|
|
62
|
-
} })] })] })] }));
|
|
58
|
+
return (_jsxs(MessageInputBox, { "aria-busy": isLoading, className: "message-input", "$inputFocused": focused, children: [_jsxs("div", { className: "wrapper-action", children: [_jsx(QuickCommandSelector, { inputRef: textAreaRef, isTrial: isTrial }), _jsx(AgentSelector, { inputRef: textAreaRef, isTrial: isTrial }), _jsxs("div", { className: listToClass(['action-box', focused && 'focused', isLoading && 'disabled']), children: [_jsx(ButtonAgent, {}), _jsx(AdaptiveTextArea, { ref: textAreaRef, placeholder: agentLabel && interpolate(t.placeholder, agentLabel), onChange: e => chat.set('nextMessage', e.target.value), value: value, onFocus: () => setFocused(true), onBlur: () => setFocused(false), onKeyDown: onKeyDown, onKeyUp: handleKeyUp, onIncreaseSize: () => setExpanded(false), onResetSize: () => !expansionLocked.current && setExpanded(true), maxHeight: isMinimized ? MIN_INPUT_HEIGHT : MAX_INPUT_HEIGHT })] })] }), _jsx(ProgressBar, { visible: true, animate: isLoading, backgroundColor: isLoading || !focused ? theme.color.light[600] : theme.color.primary[500] }), _jsx(InfoBar, {}), _jsx(ButtonBar, { focused: focused, onSend: onSend, isLoading: isLoading })] }));
|
|
63
59
|
};
|
|
64
60
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export declare const MAX_INPUT_HEIGHT = 300;
|
|
2
2
|
export declare const MIN_INPUT_HEIGHT = 24;
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const SelectionBarWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
4
|
+
$inputFocused?: boolean;
|
|
5
|
+
}>> & string;
|
|
6
|
+
export declare const MessageInputBox: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
7
|
+
$inputFocused?: boolean;
|
|
8
|
+
}>> & string;
|
|
4
9
|
//# sourceMappingURL=styled.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styled.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/styled.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"styled.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/styled.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC,eAAO,MAAM,mBAAmB;oBAA+B,OAAO;YA0CrE,CAAA;AAED,eAAO,MAAM,eAAe;oBAA+B,OAAO;YAuNjE,CAAA"}
|
|
@@ -1,17 +1,58 @@
|
|
|
1
1
|
import { IconButton } from '@citric/ui';
|
|
2
2
|
import { theme } from '@stack-spot/portal-theme';
|
|
3
3
|
import { styled } from 'styled-components';
|
|
4
|
-
const INFO_BAR_HEIGHT =
|
|
4
|
+
const INFO_BAR_HEIGHT = 38;
|
|
5
5
|
const INFO_BAR_DISPLACEMENT = 4;
|
|
6
6
|
export const MAX_INPUT_HEIGHT = 300;
|
|
7
7
|
export const MIN_INPUT_HEIGHT = 24;
|
|
8
|
+
export const SelectionBarWrapper = styled.div `
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
justify-content: space-between;
|
|
11
|
+
position: relative;
|
|
12
|
+
padding: 8px;
|
|
13
|
+
background-color: ${theme.color.light[500]};
|
|
14
|
+
border-bottom-left-radius: 4px;
|
|
15
|
+
border-bottom-right-radius: 4px;
|
|
16
|
+
border: 2px solid ${({ $inputFocused }) => theme.color.light[$inputFocused ? 600 : 500]};
|
|
17
|
+
border-top: none;
|
|
18
|
+
|
|
19
|
+
.selection-list {
|
|
20
|
+
opacity: 0;
|
|
21
|
+
transition: height 0.4s, opacity 0.3s;
|
|
22
|
+
}
|
|
23
|
+
.visible {
|
|
24
|
+
opacity: 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.selection-list-content {
|
|
28
|
+
background-color: ${theme.color.light[500]};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
ul {
|
|
32
|
+
list-style: none;
|
|
33
|
+
padding: 0;
|
|
34
|
+
margin: 12px;
|
|
35
|
+
min-width: 225px;
|
|
36
|
+
}
|
|
37
|
+
li {
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
svg {
|
|
40
|
+
width: 16px;
|
|
41
|
+
height: 16px;
|
|
42
|
+
}
|
|
43
|
+
&:hover {
|
|
44
|
+
background-color: ${theme.color.light[600]}
|
|
45
|
+
}
|
|
46
|
+
a:hover {
|
|
47
|
+
background-color: ${theme.color.light[600]}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
8
51
|
export const MessageInputBox = styled.div `
|
|
9
52
|
display: flex;
|
|
10
53
|
flex-direction: column;
|
|
11
54
|
|
|
12
55
|
> .info-bar {
|
|
13
|
-
margin-top: 8px;
|
|
14
|
-
margin-bottom: -3px;
|
|
15
56
|
position: relative;
|
|
16
57
|
overflow: hidden;
|
|
17
58
|
|
|
@@ -31,14 +72,14 @@ export const MessageInputBox = styled.div `
|
|
|
31
72
|
top: 0;
|
|
32
73
|
left: 0;
|
|
33
74
|
right: 0;
|
|
34
|
-
border-top-left-radius: 10px;
|
|
35
|
-
border-top-right-radius: 10px;
|
|
36
75
|
height: ${INFO_BAR_HEIGHT}px;
|
|
37
|
-
padding:
|
|
76
|
+
padding-top: 8px;
|
|
38
77
|
background-color: ${theme.color.light[500]};
|
|
39
78
|
display: flex;
|
|
40
79
|
flex-direction: row;
|
|
41
|
-
gap:
|
|
80
|
+
gap: 4px;
|
|
81
|
+
border-right: 2px solid ${({ $inputFocused }) => theme.color.light[$inputFocused ? 600 : 500]};
|
|
82
|
+
border-left: 2px solid ${({ $inputFocused }) => theme.color.light[$inputFocused ? 600 : 500]};
|
|
42
83
|
|
|
43
84
|
.list-overflow {
|
|
44
85
|
max-width: calc(100% - 30px); // close button + gap
|
|
@@ -91,16 +132,19 @@ export const MessageInputBox = styled.div `
|
|
|
91
132
|
flex-direction: row;
|
|
92
133
|
gap: 8px;
|
|
93
134
|
align-items: end;
|
|
94
|
-
border-radius: 4px;
|
|
95
|
-
border:
|
|
96
|
-
|
|
135
|
+
border-top-left-radius: 4px;
|
|
136
|
+
border-top-right-radius: 4px;
|
|
137
|
+
border: 2px solid ${theme.color.light[500]};
|
|
138
|
+
|
|
139
|
+
background-color: ${theme.color.light[500]};
|
|
97
140
|
padding: 10px 8px;
|
|
98
141
|
transition: border-color 0.3s, background-color 0.3s;
|
|
99
142
|
|
|
100
143
|
&.focused {
|
|
101
|
-
border
|
|
144
|
+
border: 2px solid ${theme.color.light[600]};
|
|
102
145
|
}
|
|
103
146
|
|
|
147
|
+
|
|
104
148
|
&.disabled {
|
|
105
149
|
background-color: ${theme.color.light[500]};
|
|
106
150
|
}
|
|
@@ -119,43 +163,9 @@ export const MessageInputBox = styled.div `
|
|
|
119
163
|
button {
|
|
120
164
|
border: none;
|
|
121
165
|
border-radius: 2px;
|
|
122
|
-
opacity: 0.6;
|
|
123
166
|
transition: background-color 0.2s, opacity 0.2s;
|
|
124
|
-
background-color: transparent;
|
|
125
167
|
padding: 0;
|
|
126
168
|
|
|
127
|
-
svg {
|
|
128
|
-
fill: ${theme.color.light[700]};
|
|
129
|
-
width: 16px;
|
|
130
|
-
height: 16px;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
&:hover {
|
|
134
|
-
background-color: ${theme.color.light[500]};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
&.send {
|
|
138
|
-
background-color: ${theme.color.inverse[500]};
|
|
139
|
-
&:hover {
|
|
140
|
-
opacity: 1;
|
|
141
|
-
}
|
|
142
|
-
svg {
|
|
143
|
-
fill: ${theme.color.inverse.contrastText};
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
&.expand {
|
|
148
|
-
svg {
|
|
149
|
-
transition: transform 0.3s;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
&.collapsed {
|
|
154
|
-
svg {
|
|
155
|
-
transform: rotate(180deg);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
169
|
&.agent {
|
|
160
170
|
border-radius: 50%;
|
|
161
171
|
opacity: 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styled.js","sourceRoot":"","sources":["../../../src/views/MessageInput/styled.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE1C,MAAM,eAAe,GAAG,EAAE,CAAA;AAC1B,MAAM,qBAAqB,GAAG,CAAC,CAAA;AAC/B,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAA;AACnC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAElC,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"styled.js","sourceRoot":"","sources":["../../../src/views/MessageInput/styled.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE1C,MAAM,eAAe,GAAG,EAAE,CAAA;AAC1B,MAAM,qBAAqB,GAAG,CAAC,CAAA;AAC/B,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAA;AACnC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAElC,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAA2B;;;;;sBAKlD,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;sBAGtB,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;;;;;;;;;;;;wBAYjE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;0BAgBpB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;0BAGtB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;CAG/C,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAA2B;;;;;;;;;;kBAUlD,eAAe,GAAG,qBAAqB;;;;;;;;;;;;;;gBAczC,eAAe;;0BAEL,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;gCAIhB,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;+BACpE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4BxF,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA2BM,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;0BAEtB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;4BAKpB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;4BAKtB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA2D5C,UAAU;;;;;;;;;;;;;;8BAcc,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;8BACtB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;gCAQpB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;oBAOlC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY;;;;;;;;;;;;;;;;;;;;cAoBpC,gBAAgB;;;;;;;;CAQ7B,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stack-spot/ai-chat-widget",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.1-beta.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"peerDependencies": {
|
|
13
13
|
"@citric/core": "^6.4.0",
|
|
14
14
|
"@stack-spot/portal-components": "^2.22.1",
|
|
15
|
-
"@citric/icons": "^5.
|
|
15
|
+
"@citric/icons": "^5.13.0",
|
|
16
16
|
"@stack-spot/portal-network": "^0.124.0",
|
|
17
17
|
"@citric/ui": "^6.10.2",
|
|
18
18
|
"@stack-spot/portal-theme": "^1.0.0",
|
package/src/app-metadata.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stack-spot/ai-chat-widget",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"date": "
|
|
3
|
+
"version": "1.21.1-beta.0",
|
|
4
|
+
"date": "Tue May 20 2025 14:17:41 GMT-0300 (Horário Padrão de Brasília)",
|
|
5
5
|
"dependencies": [
|
|
6
6
|
{
|
|
7
7
|
"name": "@stack-spot/app-metadata",
|
|
@@ -93,11 +93,11 @@
|
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"name": "@citric/icons",
|
|
96
|
-
"version": "5.
|
|
96
|
+
"version": "5.13.0(react@18.2.0)"
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"name": "@citric/ui",
|
|
100
|
-
"version": "6.10.2(@citric/core@6.4.0(lodash@4.17.21)(react@18.2.0)(styled-components@6.1.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0)))(@citric/icons@5.
|
|
100
|
+
"version": "6.10.2(@citric/core@6.4.0(lodash@4.17.21)(react@18.2.0)(styled-components@6.1.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0)))(@citric/icons@5.13.0(react@18.2.0))(lodash@4.17.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(styled-components@6.1.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0))"
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
"name": "@dagrejs/dagre",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
"name": "@stack-spot/portal-components",
|
|
112
|
-
"version": "2.22.1(@citric/core@6.4.0(lodash@4.17.21)(react@18.2.0)(styled-components@6.1.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0)))(@citric/icons@5.
|
|
112
|
+
"version": "2.22.1(@citric/core@6.4.0(lodash@4.17.21)(react@18.2.0)(styled-components@6.1.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0)))(@citric/icons@5.13.0(react@18.2.0))(@citric/ui@6.10.2(@citric/core@6.4.0(lodash@4.17.21)(react@18.2.0)(styled-components@6.1.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0)))(@citric/icons@5.13.0(react@18.2.0))(lodash@4.17.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(styled-components@6.1.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0)))(@stack-spot/portal-theme@1.1.0(@citric/core@6.4.0(lodash@4.17.21)(react@18.2.0)(styled-components@6.1.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(styled-components@6.1.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0)))(@stack-spot/portal-translate@1.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@types/react@18.3.11)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)"
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "@stack-spot/portal-network",
|
|
@@ -2,6 +2,10 @@ import { listToClass, theme, WithStyle } from '@stack-spot/portal-theme'
|
|
|
2
2
|
import { styled } from 'styled-components'
|
|
3
3
|
|
|
4
4
|
interface Props extends WithStyle {
|
|
5
|
+
/**
|
|
6
|
+
* Apply animation effects to the progress bar.
|
|
7
|
+
*/
|
|
8
|
+
animate?: boolean,
|
|
5
9
|
/**
|
|
6
10
|
* Whether or not the progress bar is visible.
|
|
7
11
|
*/
|
|
@@ -42,8 +46,8 @@ function gradientFromColorArray(colors: string[]) {
|
|
|
42
46
|
// the shimmering effect requires more space
|
|
43
47
|
const SHIMMER_PADDING = '10px'
|
|
44
48
|
|
|
45
|
-
const Styled = styled.div<{ $bg: string[], $fg: string[], $shimmer: string[] }>`
|
|
46
|
-
margin: 7px 0;
|
|
49
|
+
const Styled = styled.div<{ $bg: string[], $fg: string[], $shimmer: string[], $animate?: boolean }>`
|
|
50
|
+
/* margin: ${({ $shimmer }) => $shimmer.length > 0 ? '7px 0' : '0'}; */
|
|
47
51
|
opacity: 0;
|
|
48
52
|
transition: opacity 0.5s;
|
|
49
53
|
|
|
@@ -125,15 +129,15 @@ const Styled = styled.div<{ $bg: string[], $fg: string[], $shimmer: string[] }>`
|
|
|
125
129
|
&:before {
|
|
126
130
|
content: '';
|
|
127
131
|
display: block;
|
|
128
|
-
width:
|
|
132
|
+
width: 100%;
|
|
129
133
|
height: 100%;
|
|
130
|
-
background: ${({ $fg }) => gradientFromColorArray($fg)};
|
|
131
|
-
animation: slide
|
|
134
|
+
background: ${({ $fg, $animate, $bg }) => $animate ? gradientFromColorArray($fg) : $bg};
|
|
135
|
+
animation: ${({ $animate }) => $animate ? 'slide .5s infinite forwards' : 'none'};
|
|
132
136
|
}
|
|
133
137
|
|
|
134
138
|
@keyframes slide {
|
|
135
139
|
from {
|
|
136
|
-
margin-left: -
|
|
140
|
+
margin-left: -100%;
|
|
137
141
|
}
|
|
138
142
|
to {
|
|
139
143
|
margin-left: 100%;
|
|
@@ -147,6 +151,7 @@ const Styled = styled.div<{ $bg: string[], $fg: string[], $shimmer: string[] }>`
|
|
|
147
151
|
* right.
|
|
148
152
|
*/
|
|
149
153
|
export const ProgressBar = ({
|
|
154
|
+
animate,
|
|
150
155
|
visible = true,
|
|
151
156
|
shimmer,
|
|
152
157
|
backgroundColor = shimmer ? 'rgba(255, 255, 255, 0.4)' : theme.color.light[500],
|
|
@@ -161,7 +166,7 @@ export const ProgressBar = ({
|
|
|
161
166
|
const progress = <div className="progress-bar"></div>
|
|
162
167
|
const result = shimmer
|
|
163
168
|
? (
|
|
164
|
-
<div className=
|
|
169
|
+
<div className={listToClass([shimmer && 'shimmer'])}>
|
|
165
170
|
<div className="colors"></div>
|
|
166
171
|
<div className="progress-glow"></div>
|
|
167
172
|
{progress}
|
|
@@ -169,7 +174,9 @@ export const ProgressBar = ({
|
|
|
169
174
|
)
|
|
170
175
|
: progress
|
|
171
176
|
return (
|
|
172
|
-
<Styled
|
|
177
|
+
<Styled
|
|
178
|
+
className={listToClass([className, visible && 'visible'])}
|
|
179
|
+
style={style} $fg={$fg} $bg={$bg} $shimmer={$shimmer} $animate={animate}>
|
|
173
180
|
{result}
|
|
174
181
|
</Styled>
|
|
175
182
|
)
|
package/src/layout.css
CHANGED
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
background-color: var(--light-400);
|
|
32
32
|
border-radius: 4px;
|
|
33
33
|
position: relative;
|
|
34
|
-
overflow: hidden;
|
|
35
34
|
width: 100%;
|
|
36
35
|
height: 100%;
|
|
37
36
|
|
|
@@ -104,11 +103,13 @@
|
|
|
104
103
|
}
|
|
105
104
|
|
|
106
105
|
.chat-content {
|
|
106
|
+
padding-bottom: 16px;
|
|
107
107
|
display: flex;
|
|
108
108
|
flex-direction: column;
|
|
109
109
|
flex: 1;
|
|
110
110
|
flex-basis: 0;
|
|
111
111
|
overflow: auto;
|
|
112
|
+
padding-right: 12px;
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
.chat-right-panel {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Box, Button, Checkbox, Flex, IconBox, Input, Label, Radio, Text } from '@citric/core'
|
|
2
2
|
import { Cog, Copy, Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
|
|
3
|
-
import {
|
|
3
|
+
import { Badge, IconButton, Tooltip } from '@citric/ui'
|
|
4
4
|
import { agentClient } from '@stack-spot/portal-network'
|
|
5
5
|
import { listToClass } from '@stack-spot/portal-theme'
|
|
6
6
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
@@ -37,7 +37,7 @@ interface Props extends CustomMessage {
|
|
|
37
37
|
/**
|
|
38
38
|
* The name of the user currently logged in (will be used if the agent type of the message is "user").
|
|
39
39
|
*/
|
|
40
|
-
username
|
|
40
|
+
username?: string,
|
|
41
41
|
/**
|
|
42
42
|
* Whether or not this is the last message in the chat. This is important for disabling action buttons in messages that are no longer
|
|
43
43
|
* relevant.
|
|
@@ -169,14 +169,14 @@ const RenderInputsEntry = ({ isLast, entry, value, setValue, labels, setLabels }
|
|
|
169
169
|
/**
|
|
170
170
|
* Renders a message (ChatEntry) in the chat.
|
|
171
171
|
*/
|
|
172
|
-
export const ChatMessage = ({ message,
|
|
172
|
+
export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Props) => {
|
|
173
173
|
const t = useTranslate(dictionary)
|
|
174
174
|
const [liked, setLiked] = useState<boolean | undefined>()
|
|
175
175
|
const [value, setValue] = useState<string[]>(message.getValue()?.initialValue ?? [])
|
|
176
176
|
const [labels, setLabels] = useState<string[]>(message.getValue()?.initialValue ?? [])
|
|
177
177
|
const entry = useChatEntry(message)
|
|
178
178
|
const dateFormatter = useDateFormatter()
|
|
179
|
-
const userInfo = entry.agentType === 'user' ?
|
|
179
|
+
const userInfo = entry.agentType === 'user' ? <></> : <AgentInfo agent={entry.agent} />
|
|
180
180
|
const date = new Date(entry.updated ?? '')
|
|
181
181
|
const shouldShowFooter = entry.updated && !isNaN(date.getTime())
|
|
182
182
|
const ref = useRef<HTMLLIElement>(null)
|
|
@@ -185,7 +185,8 @@ export const ChatMessage = ({ message, username, isLast, beforeMessage, afterMes
|
|
|
185
185
|
const chat = useCurrentChat()
|
|
186
186
|
const agentId = entry.agent?.id ?? ''
|
|
187
187
|
const [agent] = agentClient.agentById.useStatefulQuery({ agentId, builtIn: !!entry?.agent?.builtIn }, { enabled: !!agentId })
|
|
188
|
-
|
|
188
|
+
const [copied, setCopied] = useState(false)
|
|
189
|
+
|
|
189
190
|
useChatScrollToBottomEffect(ref, [entry])
|
|
190
191
|
|
|
191
192
|
const detailKS = useCallback(({ name, slug, documentScore, documentId }: Required<TextChatEntry>['knowledgeSources'][number]) => {
|
|
@@ -250,6 +251,12 @@ export const ChatMessage = ({ message, username, isLast, beforeMessage, afterMes
|
|
|
250
251
|
)}
|
|
251
252
|
</>, [entry, isLast, runAction])
|
|
252
253
|
|
|
254
|
+
const handleCopy = () => {
|
|
255
|
+
onCopyAll(entry, chat)
|
|
256
|
+
setCopied(true)
|
|
257
|
+
setTimeout(() => setCopied(false), 1000)
|
|
258
|
+
}
|
|
259
|
+
|
|
253
260
|
const renderContent = () => {
|
|
254
261
|
if (entry.type === 'md') {
|
|
255
262
|
return <>
|
|
@@ -257,13 +264,7 @@ export const ChatMessage = ({ message, username, isLast, beforeMessage, afterMes
|
|
|
257
264
|
{renderActions()}
|
|
258
265
|
</>
|
|
259
266
|
}
|
|
260
|
-
|
|
261
|
-
return <>
|
|
262
|
-
<p className="plain-text">{entry.content}</p>
|
|
263
|
-
{renderActions()}
|
|
264
|
-
</>
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
+
|
|
267
268
|
return <form>
|
|
268
269
|
<RenderInputsEntry entry={entry} isLast={isLast} value={value} setValue={setValue} setLabels={setLabels} labels={labels} />
|
|
269
270
|
<Box mt="4">
|
|
@@ -322,22 +323,72 @@ export const ChatMessage = ({ message, username, isLast, beforeMessage, afterMes
|
|
|
322
323
|
{shouldShowFooter && <div className="message-footer">
|
|
323
324
|
{entry.agentType === 'bot' && !entry.error && <div className="message-actions">
|
|
324
325
|
{entry.type === 'md' && (
|
|
325
|
-
|
|
326
|
-
<
|
|
327
|
-
|
|
326
|
+
copied ? (
|
|
327
|
+
<Tooltip text={t.copied} sx={{ marginLeft: '14px' }}>
|
|
328
|
+
<IconButton
|
|
329
|
+
appearance="square"
|
|
330
|
+
color="light"
|
|
331
|
+
title={t.copied}
|
|
332
|
+
aria-label={t.copied}
|
|
333
|
+
onClick={handleCopy}
|
|
334
|
+
>
|
|
335
|
+
<Copy />
|
|
336
|
+
</IconButton>
|
|
337
|
+
</Tooltip>
|
|
338
|
+
) : (
|
|
339
|
+
<IconButton
|
|
340
|
+
appearance="square"
|
|
341
|
+
color="light"
|
|
342
|
+
title={t.copy}
|
|
343
|
+
aria-label={t.copy}
|
|
344
|
+
onClick={handleCopy}
|
|
345
|
+
>
|
|
346
|
+
<Copy />
|
|
347
|
+
</IconButton>
|
|
348
|
+
)
|
|
328
349
|
)}
|
|
329
350
|
{entry.messageId && (
|
|
330
351
|
<>
|
|
331
|
-
<IconButton title={t.like} aria-label={t.like} onClick={like}>
|
|
352
|
+
<IconButton appearance="square" color="light" title={t.like} aria-label={t.like} onClick={like}>
|
|
332
353
|
{liked === true ? <LikeFill /> : <Like />}
|
|
333
354
|
</IconButton>
|
|
334
|
-
<IconButton title={t.dislike} aria-label={t.dislike} onClick={dislike}>
|
|
355
|
+
<IconButton appearance="square" color="light" title={t.dislike} aria-label={t.dislike} onClick={dislike}>
|
|
335
356
|
{liked === false ? <DislikeFill /> : <Dislike />}
|
|
336
357
|
</IconButton>
|
|
337
358
|
</>
|
|
338
359
|
)}
|
|
339
360
|
</div>}
|
|
340
|
-
|
|
361
|
+
|
|
362
|
+
{entry.agentType === 'user' && (
|
|
363
|
+
<div className="message-actions">
|
|
364
|
+
{copied ? (
|
|
365
|
+
<Tooltip text={t.copied}>
|
|
366
|
+
<IconButton
|
|
367
|
+
appearance="square"
|
|
368
|
+
color="light"
|
|
369
|
+
title={t.copied}
|
|
370
|
+
aria-label={t.copied}
|
|
371
|
+
onClick={handleCopy}
|
|
372
|
+
size="sm"
|
|
373
|
+
>
|
|
374
|
+
<Copy />
|
|
375
|
+
</IconButton>
|
|
376
|
+
</Tooltip>
|
|
377
|
+
) : (
|
|
378
|
+
<IconButton
|
|
379
|
+
appearance="square"
|
|
380
|
+
color="light"
|
|
381
|
+
title={t.copy}
|
|
382
|
+
aria-label={t.copy}
|
|
383
|
+
onClick={handleCopy}
|
|
384
|
+
size="sm"
|
|
385
|
+
>
|
|
386
|
+
<Copy />
|
|
387
|
+
</IconButton>
|
|
388
|
+
)}
|
|
389
|
+
</div>
|
|
390
|
+
)}
|
|
391
|
+
<Text as="label" appearance="microtext1" className="chat-date">
|
|
341
392
|
{dateFormatter.formatForChatMessage(date)}
|
|
342
393
|
</Text>
|
|
343
394
|
</div>}
|
|
@@ -352,6 +403,7 @@ const dictionary = {
|
|
|
352
403
|
dislike: 'Dislike',
|
|
353
404
|
tools: 'Tools',
|
|
354
405
|
openToolsPanel: 'Open the tools panel to see more details.',
|
|
406
|
+
copied: 'Copied',
|
|
355
407
|
},
|
|
356
408
|
pt: {
|
|
357
409
|
copy: 'Copiar',
|
|
@@ -359,5 +411,6 @@ const dictionary = {
|
|
|
359
411
|
dislike: 'Não gostei',
|
|
360
412
|
tools: 'Ferramentas',
|
|
361
413
|
openToolsPanel: 'Abrir o painel de ferramentas para ver mais detalhes.',
|
|
414
|
+
copied: 'Copiado',
|
|
362
415
|
},
|
|
363
416
|
} satisfies Dictionary
|
package/src/views/Chat/styled.ts
CHANGED
|
@@ -11,7 +11,7 @@ export const ChatList: IStyledComponentBase<
|
|
|
11
11
|
flex-direction: column;
|
|
12
12
|
justify-content: end;
|
|
13
13
|
gap: 20px;
|
|
14
|
-
margin: 0
|
|
14
|
+
margin: 0;
|
|
15
15
|
padding: 0;
|
|
16
16
|
flex: 1;
|
|
17
17
|
|
|
@@ -61,14 +61,18 @@ export const ChatList: IStyledComponentBase<
|
|
|
61
61
|
.message-footer {
|
|
62
62
|
display: flex;
|
|
63
63
|
flex-direction: row;
|
|
64
|
+
align-items: center;
|
|
65
|
+
gap: 8px;
|
|
64
66
|
|
|
65
67
|
.message-actions {
|
|
66
68
|
display: flex;
|
|
67
69
|
flex-direction: row;
|
|
68
|
-
gap:
|
|
70
|
+
gap: 4px;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
.chat-date {
|
|
74
|
+
display: inline-block;
|
|
75
|
+
align-content: flex-end;
|
|
72
76
|
opacity: 0.6;
|
|
73
77
|
margin-left: auto;
|
|
74
78
|
}
|
|
@@ -145,9 +149,10 @@ export const ChatList: IStyledComponentBase<
|
|
|
145
149
|
gap: 8px;
|
|
146
150
|
|
|
147
151
|
.message-content {
|
|
148
|
-
padding:
|
|
152
|
+
padding: 16px;
|
|
153
|
+
border-radius: 24px;
|
|
154
|
+
border-top-right-radius: 0;
|
|
149
155
|
background-color: ${theme.color.light[500]};
|
|
150
|
-
border-radius: 4px;
|
|
151
156
|
|
|
152
157
|
.markdown > p:first-child {
|
|
153
158
|
margin-top: 0;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Flex } from '@citric/core'
|
|
2
|
+
import { ArrowUp, Code, Stop } from '@citric/icons'
|
|
3
|
+
import { IconButton } from '@citric/ui'
|
|
4
|
+
import { useCurrentChat, useCurrentChatState, useWidget } from '../../context/hooks'
|
|
5
|
+
import { useMessageInputDictionary } from './dictionary'
|
|
6
|
+
import { SelectContent } from './SelectContent'
|
|
7
|
+
import { SelectionBarWrapper } from './styled'
|
|
8
|
+
|
|
9
|
+
interface SelectionBarProps {
|
|
10
|
+
/**
|
|
11
|
+
* A function to run when the send button is clicked.
|
|
12
|
+
*/
|
|
13
|
+
onSend: () => void,
|
|
14
|
+
/**
|
|
15
|
+
* Whether or not the message is currently being sent. This is used to decide which button to show: send or cancel.
|
|
16
|
+
*/
|
|
17
|
+
isLoading?: boolean,
|
|
18
|
+
/**
|
|
19
|
+
* State to determine whether the input is focused
|
|
20
|
+
*/
|
|
21
|
+
focused?: boolean,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ButtonBar = ({ onSend, isLoading, focused }: SelectionBarProps) => {
|
|
25
|
+
const t = useMessageInputDictionary()
|
|
26
|
+
const chat = useCurrentChat()
|
|
27
|
+
const widget = useWidget()
|
|
28
|
+
const features = useCurrentChatState('features')
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<SelectionBarWrapper className="button-group" $inputFocused={focused}>
|
|
32
|
+
<Flex sx={{ gap: '4px' }}>
|
|
33
|
+
<SelectContent />
|
|
34
|
+
{features.editor && (
|
|
35
|
+
<IconButton color="light" appearance="square" aria-label={t.code} title={t.code} onClick={() => widget.set('panel', 'editor')}>
|
|
36
|
+
<Code />
|
|
37
|
+
</IconButton>
|
|
38
|
+
)}
|
|
39
|
+
</Flex>
|
|
40
|
+
<Flex>
|
|
41
|
+
{isLoading ? (
|
|
42
|
+
<IconButton appearance="square" color="inverse" aria-label={t.cancel}
|
|
43
|
+
onClick={() => chat.abort()} className="send" title={t.cancel}>
|
|
44
|
+
<Stop />
|
|
45
|
+
</IconButton>
|
|
46
|
+
) : (
|
|
47
|
+
<IconButton appearance="square" color="inverse" aria-label={t.send}
|
|
48
|
+
onClick={onSend} className="send" title={t.send}>
|
|
49
|
+
<ArrowUp />
|
|
50
|
+
</IconButton>
|
|
51
|
+
)}
|
|
52
|
+
</Flex>
|
|
53
|
+
</SelectionBarWrapper>
|
|
54
|
+
)
|
|
55
|
+
}
|