@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.
Files changed (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/app-metadata.json +5 -5
  3. package/dist/components/ProgressBar.d.ts +5 -1
  4. package/dist/components/ProgressBar.d.ts.map +1 -1
  5. package/dist/components/ProgressBar.js +8 -8
  6. package/dist/components/ProgressBar.js.map +1 -1
  7. package/dist/layout.css +2 -1
  8. package/dist/views/Chat/ChatMessage.d.ts +2 -2
  9. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  10. package/dist/views/Chat/ChatMessage.js +12 -7
  11. package/dist/views/Chat/ChatMessage.js.map +1 -1
  12. package/dist/views/Chat/styled.d.ts.map +1 -1
  13. package/dist/views/Chat/styled.js +9 -4
  14. package/dist/views/Chat/styled.js.map +1 -1
  15. package/dist/views/MessageInput/ButtonBar.d.ts +17 -0
  16. package/dist/views/MessageInput/ButtonBar.d.ts.map +1 -0
  17. package/dist/views/MessageInput/ButtonBar.js +16 -0
  18. package/dist/views/MessageInput/ButtonBar.js.map +1 -0
  19. package/dist/views/MessageInput/InfoBar.d.ts.map +1 -1
  20. package/dist/views/MessageInput/InfoBar.js +2 -2
  21. package/dist/views/MessageInput/InfoBar.js.map +1 -1
  22. package/dist/views/MessageInput/SelectContent.d.ts +2 -0
  23. package/dist/views/MessageInput/SelectContent.d.ts.map +1 -0
  24. package/dist/views/MessageInput/SelectContent.js +60 -0
  25. package/dist/views/MessageInput/SelectContent.js.map +1 -0
  26. package/dist/views/MessageInput/dictionary.js +2 -2
  27. package/dist/views/MessageInput/dictionary.js.map +1 -1
  28. package/dist/views/MessageInput/index.d.ts.map +1 -1
  29. package/dist/views/MessageInput/index.js +4 -8
  30. package/dist/views/MessageInput/index.js.map +1 -1
  31. package/dist/views/MessageInput/styled.d.ts +6 -1
  32. package/dist/views/MessageInput/styled.d.ts.map +1 -1
  33. package/dist/views/MessageInput/styled.js +55 -45
  34. package/dist/views/MessageInput/styled.js.map +1 -1
  35. package/package.json +2 -2
  36. package/src/app-metadata.json +5 -5
  37. package/src/components/ProgressBar.tsx +15 -8
  38. package/src/layout.css +2 -1
  39. package/src/views/Chat/ChatMessage.tsx +71 -18
  40. package/src/views/Chat/styled.ts +9 -4
  41. package/src/views/MessageInput/ButtonBar.tsx +55 -0
  42. package/src/views/MessageInput/InfoBar.tsx +6 -2
  43. package/src/views/MessageInput/SelectContent.tsx +88 -0
  44. package/src/views/MessageInput/dictionary.ts +2 -2
  45. package/src/views/MessageInput/index.tsx +8 -18
  46. package/src/views/MessageInput/styled.ts +57 -46
  47. package/dist/views/MessageInput/ButtonGroup.d.ts +0 -29
  48. package/dist/views/MessageInput/ButtonGroup.d.ts.map +0 -1
  49. package/dist/views/MessageInput/ButtonGroup.js +0 -34
  50. package/dist/views/MessageInput/ButtonGroup.js.map +0 -1
  51. package/src/views/MessageInput/ButtonGroup.tsx +0 -109
@@ -1,5 +1,5 @@
1
1
  import { Text } from '@citric/core'
2
- import { Times, TimesMini } from '@citric/icons'
2
+ import { TimesMini } from '@citric/icons'
3
3
  import { Badge, IconButton } from '@citric/ui'
4
4
  import { loader } from '@monaco-editor/react'
5
5
  import { ColorPaletteName, listToClass } from '@stack-spot/portal-theme'
@@ -73,7 +73,11 @@ export const InfoBar = () => {
73
73
  <div className="space"></div>
74
74
  <div className="content">
75
75
  {shouldRenderRemoveAllButton && (
76
- <IconButton aria-label={t.removeConfig} title={t.removeConfig} onClick={removeAll}><Times /></IconButton>
76
+ <IconButton
77
+ appearance="square" color="light"
78
+ aria-label={t.removeConfig} title={t.removeConfig} onClick={removeAll}>
79
+ <TimesMini />
80
+ </IconButton>
77
81
  )}
78
82
  <FadingOverflow className="list-overflow" scroll="arrows" enableHorizontalScrollWithVerticalWheel>
79
83
  <ul>
@@ -0,0 +1,88 @@
1
+ import { Clip, KnowledgeSource, Spaces, Stack } from '@citric/icons'
2
+ import { IconButton } from '@citric/ui'
3
+ import { SelectionList } from '@stack-spot/portal-components/SelectionList'
4
+ import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
5
+ import { useMemo, useState } from 'react'
6
+ import { useCurrentChatState, useWidget } from '../../context/hooks'
7
+
8
+ type chatFeatures = 'workspace' | 'knowledgeSource' | 'stack'
9
+ type chatPanel = 'ks' | 'workspace' | 'stack'
10
+
11
+ export const SelectContent = () => {
12
+ const widget = useWidget()
13
+ const [visibleMenu, setVisibleMenu] = useState(false)
14
+ const features = useCurrentChatState('features')
15
+ const hasFeatureButtons = features.workspace || features.knowledgeSource || features.stack
16
+ const t = useTranslate(dictionary)
17
+
18
+ const itemConfigs = [
19
+ {
20
+ key: 'knowledgeSource',
21
+ label: 'Knowledge Sources',
22
+ icon: <KnowledgeSource />,
23
+ panel: 'ks',
24
+ },
25
+ {
26
+ key: 'stack',
27
+ label: 'Stacks AI',
28
+ icon: <Stack />,
29
+ panel: 'stack',
30
+ },
31
+ {
32
+ key: 'workspace',
33
+ label: 'Spots',
34
+ icon: <Spaces />,
35
+ panel: 'workspace',
36
+ },
37
+ ]
38
+
39
+ const listItems = useMemo(() =>
40
+ itemConfigs.filter(chatFeatures => features[chatFeatures.key as chatFeatures])
41
+ .map(chatFeatures => ({
42
+ label: chatFeatures.label,
43
+ icon: chatFeatures.icon,
44
+ onClick: () => {
45
+ widget.set('panel', chatFeatures.panel as chatPanel)
46
+ setVisibleMenu(false)
47
+ },
48
+ })), [features, widget])
49
+
50
+ if (!hasFeatureButtons) return null
51
+
52
+ return (
53
+ <>
54
+ <IconButton
55
+ color="light"
56
+ appearance="square"
57
+ role="button"
58
+ title={visibleMenu ? t.collapse : t.expand}
59
+ data-test-hint="button-options"
60
+ aria-label={visibleMenu ? t.collapse : t.expand}
61
+ onClick={() => setVisibleMenu(state => !state)}>
62
+ <Clip />
63
+ </IconButton>
64
+ <SelectionList
65
+ style={{
66
+ position: 'absolute',
67
+ top: '-140px',
68
+ }}
69
+ id="menuConfig"
70
+ visible={visibleMenu}
71
+ onHide={() => setVisibleMenu(false)}
72
+ items={listItems}
73
+ />
74
+ </>
75
+ )
76
+ }
77
+
78
+ const dictionary = {
79
+ en: {
80
+ expand: 'Expand options',
81
+ collapse: 'Collapse options',
82
+ },
83
+ pt: {
84
+ expand: 'Mostrar opções',
85
+ collapse: 'Esconder opções',
86
+ },
87
+ } satisfies Dictionary
88
+
@@ -10,7 +10,7 @@ const dictionary = {
10
10
  collapse: 'Hide buttons',
11
11
  expand: 'Show buttons',
12
12
  send: 'Send message',
13
- placeholder: 'Message to $0',
13
+ placeholder: 'Message to $0 or use / or @.',
14
14
  cancel: 'Cancel',
15
15
  removeConfig: 'Remove all the configuration',
16
16
  removeStack: 'Stop using the current stack',
@@ -29,7 +29,7 @@ const dictionary = {
29
29
  collapse: 'Esconder botões',
30
30
  expand: 'Mostrar botões',
31
31
  send: 'Enviar mensagem',
32
- placeholder: 'Mensagem para $0',
32
+ placeholder: 'Mensagem para $0 ou use / ou @.',
33
33
  cancel: 'Cancelar',
34
34
  removeConfig: 'Remover todas as configurações',
35
35
  removeStack: 'Parar de usar a stack atual',
@@ -1,4 +1,4 @@
1
- import { listToClass } from '@stack-spot/portal-theme'
1
+ import { listToClass, theme } from '@stack-spot/portal-theme'
2
2
  import { interpolate } from '@stack-spot/portal-translate'
3
3
  import { useCallback, useEffect, useRef, useState } from 'react'
4
4
  import { AdaptiveTextArea } from '../../components/AdaptiveTextArea'
@@ -9,7 +9,7 @@ import { ChatEntry } from '../../state/ChatEntry'
9
9
  import { checkIsTrial } from '../../utils/check-is-trial'
10
10
  import { AgentSelector } from './AgentSelector'
11
11
  import { ButtonAgent } from './ButtonAgent'
12
- import { ButtonGroup } from './ButtonGroup'
12
+ import { ButtonBar } from './ButtonBar'
13
13
  import { useUserEntryHistoryShortcut } from './chat-entry-history'
14
14
  import { useMessageInputDictionary } from './dictionary'
15
15
  import { InfoBar } from './InfoBar'
@@ -24,7 +24,7 @@ import { MAX_INPUT_HEIGHT, MessageInputBox, MIN_INPUT_HEIGHT } from './styled'
24
24
  export const MessageInput = () => {
25
25
  const t = useMessageInputDictionary()
26
26
  const [focused, setFocused] = useState(false)
27
- const [expanded, setExpanded] = useState(true)
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
 
49
48
  const onKeyDown = useCallback((event: React.KeyboardEvent<HTMLTextAreaElement>) => {
@@ -60,9 +59,7 @@ export const MessageInput = () => {
60
59
  }, [isLoading])
61
60
 
62
61
  return (
63
- <MessageInputBox aria-busy={isLoading} className="message-input">
64
- <ProgressBar visible={isLoading} shimmer />
65
- <InfoBar />
62
+ <MessageInputBox aria-busy={isLoading} className="message-input" $inputFocused={focused}>
66
63
  <div className="wrapper-action">
67
64
  <QuickCommandSelector inputRef={textAreaRef} isTrial={isTrial} />
68
65
  <AgentSelector inputRef={textAreaRef} isTrial={isTrial} />
@@ -70,7 +67,6 @@ export const MessageInput = () => {
70
67
  <ButtonAgent />
71
68
  <AdaptiveTextArea
72
69
  ref={textAreaRef}
73
- disabled={isLoading}
74
70
  placeholder={agentLabel && interpolate(t.placeholder, agentLabel)}
75
71
  onChange={e => chat.set('nextMessage', e.target.value)}
76
72
  value={value}
@@ -82,18 +78,12 @@ export const MessageInput = () => {
82
78
  onResetSize={() => !expansionLocked.current && setExpanded(true)}
83
79
  maxHeight={isMinimized ? MIN_INPUT_HEIGHT : MAX_INPUT_HEIGHT}
84
80
  />
85
- <ButtonGroup
86
- onSend={onSend}
87
- onCancel={() => chat.abort()}
88
- expanded={expanded}
89
- isLoading={isLoading}
90
- setExpanded={(value) => {
91
- setExpanded(value)
92
- expansionLocked.current = expanded
93
- }}
94
- />
95
81
  </div>
96
82
  </div>
83
+ <ProgressBar visible={true} animate={isLoading}
84
+ backgroundColor={isLoading || !focused ? theme.color.light[600] : theme.color.primary[500]} />
85
+ <InfoBar />
86
+ <ButtonBar focused={focused} onSend={onSend} isLoading={isLoading} />
97
87
  </MessageInputBox>
98
88
  )
99
89
  }
@@ -2,18 +2,60 @@ import { IconButton } from '@citric/ui'
2
2
  import { theme } from '@stack-spot/portal-theme'
3
3
  import { styled } from 'styled-components'
4
4
 
5
- const INFO_BAR_HEIGHT = 42
5
+ const INFO_BAR_HEIGHT = 38
6
6
  const INFO_BAR_DISPLACEMENT = 4
7
7
  export const MAX_INPUT_HEIGHT = 300
8
8
  export const MIN_INPUT_HEIGHT = 24
9
9
 
10
- export const MessageInputBox = styled.div`
10
+ export const SelectionBarWrapper = styled.div<{$inputFocused?: boolean}>`
11
+ display: inline-flex;
12
+ justify-content: space-between;
13
+ position: relative;
14
+ padding: 8px;
15
+ background-color: ${theme.color.light[500]};
16
+ border-bottom-left-radius: 4px;
17
+ border-bottom-right-radius: 4px;
18
+ border: 2px solid ${({ $inputFocused }) => theme.color.light[$inputFocused ? 600 : 500]};
19
+ border-top: none;
20
+
21
+ .selection-list {
22
+ opacity: 0;
23
+ transition: height 0.4s, opacity 0.3s;
24
+ }
25
+ .visible {
26
+ opacity: 1;
27
+ }
28
+
29
+ .selection-list-content {
30
+ background-color: ${theme.color.light[500]};
31
+ }
32
+
33
+ ul {
34
+ list-style: none;
35
+ padding: 0;
36
+ margin: 12px;
37
+ min-width: 225px;
38
+ }
39
+ li {
40
+ cursor: pointer;
41
+ svg {
42
+ width: 16px;
43
+ height: 16px;
44
+ }
45
+ &:hover {
46
+ background-color: ${theme.color.light[600]}
47
+ }
48
+ a:hover {
49
+ background-color: ${theme.color.light[600]}
50
+ }
51
+ }
52
+ `
53
+
54
+ export const MessageInputBox = styled.div<{$inputFocused?: boolean}>`
11
55
  display: flex;
12
56
  flex-direction: column;
13
57
 
14
58
  > .info-bar {
15
- margin-top: 8px;
16
- margin-bottom: -3px;
17
59
  position: relative;
18
60
  overflow: hidden;
19
61
 
@@ -33,14 +75,14 @@ export const MessageInputBox = styled.div`
33
75
  top: 0;
34
76
  left: 0;
35
77
  right: 0;
36
- border-top-left-radius: 10px;
37
- border-top-right-radius: 10px;
38
78
  height: ${INFO_BAR_HEIGHT}px;
39
- padding: 6px 4px 0;
79
+ padding-top: 8px;
40
80
  background-color: ${theme.color.light[500]};
41
81
  display: flex;
42
82
  flex-direction: row;
43
- gap: 6px;
83
+ gap: 4px;
84
+ border-right: 2px solid ${({ $inputFocused }) => theme.color.light[$inputFocused ? 600 : 500]};
85
+ border-left: 2px solid ${({ $inputFocused }) => theme.color.light[$inputFocused ? 600 : 500]};
44
86
 
45
87
  .list-overflow {
46
88
  max-width: calc(100% - 30px); // close button + gap
@@ -93,16 +135,19 @@ export const MessageInputBox = styled.div`
93
135
  flex-direction: row;
94
136
  gap: 8px;
95
137
  align-items: end;
96
- border-radius: 4px;
97
- border: 1px solid ${theme.color.light[500]};
98
- background-color: ${theme.color.light[300]};
138
+ border-top-left-radius: 4px;
139
+ border-top-right-radius: 4px;
140
+ border: 2px solid ${theme.color.light[500]};
141
+
142
+ background-color: ${theme.color.light[500]};
99
143
  padding: 10px 8px;
100
144
  transition: border-color 0.3s, background-color 0.3s;
101
145
 
102
146
  &.focused {
103
- border-color: ${theme.color.primary[500]};
147
+ border: 2px solid ${theme.color.light[600]};
104
148
  }
105
149
 
150
+
106
151
  &.disabled {
107
152
  background-color: ${theme.color.light[500]};
108
153
  }
@@ -121,43 +166,9 @@ export const MessageInputBox = styled.div`
121
166
  button {
122
167
  border: none;
123
168
  border-radius: 2px;
124
- opacity: 0.6;
125
169
  transition: background-color 0.2s, opacity 0.2s;
126
- background-color: transparent;
127
170
  padding: 0;
128
171
 
129
- svg {
130
- fill: ${theme.color.light[700]};
131
- width: 16px;
132
- height: 16px;
133
- }
134
-
135
- &:hover {
136
- background-color: ${theme.color.light[500]};
137
- }
138
-
139
- &.send {
140
- background-color: ${theme.color.inverse[500]};
141
- &:hover {
142
- opacity: 1;
143
- }
144
- svg {
145
- fill: ${theme.color.inverse.contrastText};
146
- }
147
- }
148
-
149
- &.expand {
150
- svg {
151
- transition: transform 0.3s;
152
- }
153
- }
154
-
155
- &.collapsed {
156
- svg {
157
- transform: rotate(180deg);
158
- }
159
- }
160
-
161
172
  &.agent {
162
173
  border-radius: 50%;
163
174
  opacity: 1;
@@ -1,29 +0,0 @@
1
- interface ButtonGroupProps {
2
- /**
3
- * Whether or not the button group is expanded.
4
- */
5
- expanded: boolean;
6
- /**
7
- * A function to set the button group as expanded or collapsed.
8
- */
9
- setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
10
- /**
11
- * Whether or not the message is currently being sent. This is used to decide which button to show: send or cancel.
12
- */
13
- isLoading: boolean;
14
- /**
15
- * A function to run when the send button is clicked.
16
- */
17
- onSend: () => void;
18
- /**
19
- * A function to run when the cancel button is clicked.
20
- */
21
- onCancel: () => void;
22
- }
23
- /**
24
- * Renders the button group at right bottom side of the message input. This includes the send button as well as the buttons to open the
25
- * editor, change the stack, etc.
26
- */
27
- export declare const ButtonGroup: ({ onSend, onCancel, expanded, setExpanded, isLoading }: ButtonGroupProps) => import("react/jsx-runtime").JSX.Element;
28
- export {};
29
- //# sourceMappingURL=ButtonGroup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ButtonGroup.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/ButtonGroup.tsx"],"names":[],"mappings":"AAOA,UAAU,gBAAgB;IACxB;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3D;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB;;OAEG;IACH,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,2DAA4D,gBAAgB,4CA0EnG,CAAA"}
@@ -1,34 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { ChevronRight, Code, KnowledgeSource, Send, Spaces, Stack, Times } from '@citric/icons';
3
- import { IconButton } from '@citric/ui';
4
- import { listToClass } from '@stack-spot/portal-theme';
5
- import { useEffect, useRef } from 'react';
6
- import { useCurrentChatState, useWidget } from '../../context/hooks.js';
7
- import { useMessageInputDictionary } from './dictionary.js';
8
- /**
9
- * Renders the button group at right bottom side of the message input. This includes the send button as well as the buttons to open the
10
- * editor, change the stack, etc.
11
- */
12
- export const ButtonGroup = ({ onSend, onCancel, expanded, setExpanded, isLoading }) => {
13
- const t = useMessageInputDictionary();
14
- const widget = useWidget();
15
- const featureButtonsWidth = useRef();
16
- const featureButtons = useRef(null);
17
- const features = useCurrentChatState('features');
18
- const hasFeatureButtons = features.workspace || features.knowledgeSource || features.stack || features.editor;
19
- useEffect(() => {
20
- if (!featureButtons.current)
21
- return;
22
- const isHidden = featureButtons.current.style.width === '0px';
23
- featureButtons.current.style.width = 'auto';
24
- featureButtonsWidth.current = featureButtons.current.clientWidth;
25
- if (isHidden)
26
- featureButtons.current.style.width = '0px';
27
- else
28
- featureButtons.current.style.width = `${featureButtonsWidth.current}px`;
29
- },
30
- // don't use the whole features object here, it would make every chat tab change rerun this effect.
31
- [features.workspace, features.knowledgeSource, features.stack, features.editor]);
32
- return (_jsxs("div", { className: "button-group", children: [hasFeatureButtons && _jsxs(_Fragment, { children: [_jsxs("div", { ref: featureButtons, className: listToClass(['feature-buttons', expanded && 'expanded']), style: { width: expanded ? featureButtonsWidth.current : 0 }, children: [features.workspace && (_jsx(IconButton, { "aria-label": t.spot, title: t.spot, onClick: () => widget.set('panel', 'workspace'), children: _jsx(Spaces, {}) })), features.knowledgeSource && (_jsx(IconButton, { "aria-label": t.knowledgeSource, title: t.knowledgeSource, onClick: () => widget.set('panel', 'ks'), children: _jsx(KnowledgeSource, {}) })), features.stack && (_jsx(IconButton, { "aria-label": t.stack, title: t.stack, onClick: () => widget.set('panel', 'stack'), children: _jsx(Stack, {}) })), features.editor && (_jsx(IconButton, { "aria-label": t.code, title: t.code, onClick: () => widget.set('panel', 'editor'), children: _jsx(Code, {}) }))] }), _jsx(IconButton, { title: expanded ? t.collapse : t.expand, className: listToClass(['expand', !expanded && 'collapsed']), "aria-label": expanded ? t.collapse : t.expand, onClick: () => setExpanded(v => !v), children: _jsx(ChevronRight, {}) })] }), isLoading ? (_jsx(IconButton, { "aria-label": t.cancel, onClick: onCancel, className: "send", title: t.cancel, children: _jsx(Times, {}) })) : (_jsx(IconButton, { "aria-label": t.send, onClick: onSend, className: "send", title: t.send, children: _jsx(Send, {}) }))] }));
33
- };
34
- //# sourceMappingURL=ButtonGroup.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ButtonGroup.js","sourceRoot":"","sources":["../../../src/views/MessageInput/ButtonGroup.tsx"],"names":[],"mappings":"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"}
@@ -1,109 +0,0 @@
1
- import { ChevronRight, Code, KnowledgeSource, Send, Spaces, Stack, Times } from '@citric/icons'
2
- import { IconButton } from '@citric/ui'
3
- import { listToClass } from '@stack-spot/portal-theme'
4
- import { useEffect, useRef } from 'react'
5
- import { useCurrentChatState, useWidget } from '../../context/hooks'
6
- import { useMessageInputDictionary } from './dictionary'
7
-
8
- interface ButtonGroupProps {
9
- /**
10
- * Whether or not the button group is expanded.
11
- */
12
- expanded: boolean,
13
- /**
14
- * A function to set the button group as expanded or collapsed.
15
- */
16
- setExpanded: React.Dispatch<React.SetStateAction<boolean>>,
17
- /**
18
- * Whether or not the message is currently being sent. This is used to decide which button to show: send or cancel.
19
- */
20
- isLoading: boolean,
21
- /**
22
- * A function to run when the send button is clicked.
23
- */
24
- onSend: () => void,
25
- /**
26
- * A function to run when the cancel button is clicked.
27
- */
28
- onCancel: () => void,
29
- }
30
-
31
- /**
32
- * Renders the button group at right bottom side of the message input. This includes the send button as well as the buttons to open the
33
- * editor, change the stack, etc.
34
- */
35
- export const ButtonGroup = ({ onSend, onCancel, expanded, setExpanded, isLoading }: ButtonGroupProps) => {
36
- const t = useMessageInputDictionary()
37
- const widget = useWidget()
38
- const featureButtonsWidth = useRef<number | undefined>()
39
- const featureButtons = useRef<HTMLDivElement>(null)
40
- const features = useCurrentChatState('features')
41
- const hasFeatureButtons = features.workspace || features.knowledgeSource || features.stack || features.editor
42
-
43
- useEffect(
44
- () => {
45
- if (!featureButtons.current) return
46
- const isHidden = featureButtons.current.style.width === '0px'
47
- featureButtons.current.style.width = 'auto'
48
- featureButtonsWidth.current = featureButtons.current.clientWidth
49
- if (isHidden) featureButtons.current.style.width = '0px'
50
- else featureButtons.current.style.width = `${featureButtonsWidth.current}px`
51
- },
52
- // don't use the whole features object here, it would make every chat tab change rerun this effect.
53
- [features.workspace, features.knowledgeSource, features.stack, features.editor],
54
- )
55
-
56
- return (
57
- <div className="button-group">
58
- {hasFeatureButtons && <>
59
- <div
60
- ref={featureButtons}
61
- className={listToClass(['feature-buttons', expanded && 'expanded'])}
62
- style={{ width: expanded ? featureButtonsWidth.current : 0 }}
63
- >
64
- {features.workspace && (
65
- <IconButton aria-label={t.spot} title={t.spot} onClick={() => widget.set('panel', 'workspace')}>
66
- <Spaces />
67
- </IconButton>
68
- )}
69
- {features.knowledgeSource && (
70
- <IconButton
71
- aria-label={t.knowledgeSource}
72
- title={t.knowledgeSource}
73
- onClick={() => widget.set('panel', 'ks')}
74
- >
75
- <KnowledgeSource />
76
- </IconButton>
77
- )}
78
- {features.stack && (
79
- <IconButton aria-label={t.stack} title={t.stack} onClick={() => widget.set('panel', 'stack')}>
80
- <Stack />
81
- </IconButton>
82
- )}
83
- {features.editor && (
84
- <IconButton aria-label={t.code} title={t.code} onClick={() => widget.set('panel', 'editor')}>
85
- <Code />
86
- </IconButton>
87
- )}
88
- </div>
89
- <IconButton
90
- title={expanded ? t.collapse : t.expand}
91
- className={listToClass(['expand', !expanded && 'collapsed'])}
92
- aria-label={expanded ? t.collapse : t.expand}
93
- onClick={() => setExpanded(v => !v)}
94
- >
95
- <ChevronRight />
96
- </IconButton>
97
- </>}
98
- {isLoading ? (
99
- <IconButton aria-label={t.cancel} onClick={onCancel} className="send" title={t.cancel}>
100
- <Times />
101
- </IconButton>
102
- ) : (
103
- <IconButton aria-label={t.send} onClick={onSend} className="send" title={t.send}>
104
- <Send />
105
- </IconButton>
106
- )}
107
- </div>
108
- )
109
- }