@stack-spot/ai-chat-widget 0.11.0 → 1.0.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 (108) hide show
  1. package/dist/chat-interceptors/quick-commands.d.ts.map +1 -1
  2. package/dist/chat-interceptors/quick-commands.js +23 -22
  3. package/dist/chat-interceptors/quick-commands.js.map +1 -1
  4. package/dist/chat-interceptors/send-message.d.ts.map +1 -1
  5. package/dist/chat-interceptors/send-message.js +14 -10
  6. package/dist/chat-interceptors/send-message.js.map +1 -1
  7. package/dist/components/AdaptiveTextArea.d.ts +1 -1
  8. package/dist/components/AdaptiveTextArea.d.ts.map +1 -1
  9. package/dist/components/AdaptiveTextArea.js +6 -4
  10. package/dist/components/AdaptiveTextArea.js.map +1 -1
  11. package/dist/components/AutoFocus.d.ts +6 -0
  12. package/dist/components/AutoFocus.d.ts.map +1 -0
  13. package/dist/components/AutoFocus.js +15 -0
  14. package/dist/components/AutoFocus.js.map +1 -0
  15. package/dist/components/Fading.d.ts +15 -0
  16. package/dist/components/Fading.d.ts.map +1 -0
  17. package/dist/components/Fading.js +31 -0
  18. package/dist/components/Fading.js.map +1 -0
  19. package/dist/components/FallbackBoundary/ErrorBoundary.d.ts +3 -0
  20. package/dist/components/FallbackBoundary/ErrorBoundary.d.ts.map +1 -1
  21. package/dist/components/FallbackBoundary/ErrorBoundary.js +18 -4
  22. package/dist/components/FallbackBoundary/ErrorBoundary.js.map +1 -1
  23. package/dist/components/FallbackBoundary/Loading.js +1 -1
  24. package/dist/components/FallbackBoundary/Loading.js.map +1 -1
  25. package/dist/components/FallbackBoundary/index.d.ts +6 -1
  26. package/dist/components/FallbackBoundary/index.d.ts.map +1 -1
  27. package/dist/components/FallbackBoundary/index.js +1 -1
  28. package/dist/components/FallbackBoundary/index.js.map +1 -1
  29. package/dist/components/OverlayMenu.d.ts +1 -1
  30. package/dist/components/OverlayMenu.d.ts.map +1 -1
  31. package/dist/components/OverlayMenu.js +26 -9
  32. package/dist/components/OverlayMenu.js.map +1 -1
  33. package/dist/components/RightPanelForm.d.ts.map +1 -1
  34. package/dist/components/RightPanelForm.js +5 -4
  35. package/dist/components/RightPanelForm.js.map +1 -1
  36. package/dist/components/Tooltip/Tooltip.d.ts +3 -1
  37. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
  38. package/dist/components/Tooltip/Tooltip.js +14 -5
  39. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  40. package/dist/components/Tooltip/TooltipAPI.d.ts +2 -2
  41. package/dist/components/Tooltip/TooltipAPI.d.ts.map +1 -1
  42. package/dist/components/Tooltip/TooltipAPI.js +51 -51
  43. package/dist/components/Tooltip/TooltipAPI.js.map +1 -1
  44. package/dist/layout.css +5 -0
  45. package/dist/regex.d.ts +2 -0
  46. package/dist/regex.d.ts.map +1 -0
  47. package/dist/regex.js +2 -0
  48. package/dist/regex.js.map +1 -0
  49. package/dist/right-panel/DefaultPanel.d.ts.map +1 -1
  50. package/dist/right-panel/DefaultPanel.js +3 -1
  51. package/dist/right-panel/DefaultPanel.js.map +1 -1
  52. package/dist/right-panel/constants.d.ts +2 -0
  53. package/dist/right-panel/constants.d.ts.map +1 -0
  54. package/dist/right-panel/constants.js +2 -0
  55. package/dist/right-panel/constants.js.map +1 -0
  56. package/dist/right-panel/hooks.d.ts.map +1 -1
  57. package/dist/right-panel/hooks.js +2 -1
  58. package/dist/right-panel/hooks.js.map +1 -1
  59. package/dist/utils/chat.js +1 -1
  60. package/dist/utils/url.d.ts +2 -0
  61. package/dist/utils/url.d.ts.map +1 -0
  62. package/dist/utils/url.js +8 -0
  63. package/dist/utils/url.js.map +1 -0
  64. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  65. package/dist/views/Chat/ChatMessage.js +24 -2
  66. package/dist/views/Chat/ChatMessage.js.map +1 -1
  67. package/dist/views/ChatHistory/ChatHistoryPanel.d.ts.map +1 -1
  68. package/dist/views/ChatHistory/ChatHistoryPanel.js +2 -1
  69. package/dist/views/ChatHistory/ChatHistoryPanel.js.map +1 -1
  70. package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
  71. package/dist/views/ChatHistory/HistoryItem.js +10 -1
  72. package/dist/views/ChatHistory/HistoryItem.js.map +1 -1
  73. package/dist/views/MessageInput/QuickCommandSelector.d.ts +6 -0
  74. package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -0
  75. package/dist/views/MessageInput/QuickCommandSelector.js +137 -0
  76. package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -0
  77. package/dist/views/MessageInput/index.d.ts.map +1 -1
  78. package/dist/views/MessageInput/index.js +6 -4
  79. package/dist/views/MessageInput/index.js.map +1 -1
  80. package/dist/views/MessageInput/styled.d.ts.map +1 -1
  81. package/dist/views/MessageInput/styled.js +137 -0
  82. package/dist/views/MessageInput/styled.js.map +1 -1
  83. package/package.json +2 -2
  84. package/src/chat-interceptors/quick-commands.ts +24 -23
  85. package/src/chat-interceptors/send-message.ts +23 -11
  86. package/src/components/AdaptiveTextArea.tsx +9 -4
  87. package/src/components/AutoFocus.tsx +20 -0
  88. package/src/components/Fading.tsx +46 -0
  89. package/src/components/FallbackBoundary/ErrorBoundary.tsx +26 -3
  90. package/src/components/FallbackBoundary/Loading.tsx +1 -1
  91. package/src/components/FallbackBoundary/index.tsx +7 -2
  92. package/src/components/OverlayMenu.tsx +59 -19
  93. package/src/components/RightPanelForm.tsx +12 -9
  94. package/src/components/Tooltip/Tooltip.tsx +24 -5
  95. package/src/components/Tooltip/TooltipAPI.ts +42 -42
  96. package/src/layout.css +5 -0
  97. package/src/regex.ts +1 -0
  98. package/src/right-panel/DefaultPanel.tsx +14 -9
  99. package/src/right-panel/constants.ts +1 -0
  100. package/src/right-panel/hooks.tsx +2 -1
  101. package/src/utils/chat.ts +1 -1
  102. package/src/utils/url.ts +8 -0
  103. package/src/views/Chat/ChatMessage.tsx +29 -5
  104. package/src/views/ChatHistory/ChatHistoryPanel.tsx +3 -2
  105. package/src/views/ChatHistory/HistoryItem.tsx +11 -2
  106. package/src/views/MessageInput/QuickCommandSelector.tsx +210 -0
  107. package/src/views/MessageInput/index.tsx +8 -4
  108. package/src/views/MessageInput/styled.ts +137 -0
@@ -0,0 +1,137 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { IconBox, Text } from '@citric/core';
3
+ import { ExternalLink, QuickCommand } from '@citric/icons';
4
+ import { IconButton } from '@citric/ui';
5
+ import { useKeyboardControls } from '@stack-spot/portal-components';
6
+ import { aiClient } from '@stack-spot/portal-network';
7
+ import { useTranslate } from '@stack-spot/portal-translate';
8
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
9
+ import { Fading } from '../../components/Fading.js';
10
+ import { FallbackBoundary } from '../../components/FallbackBoundary/index.js';
11
+ import { useCurrentChat, useCurrentChatState } from '../../context/hooks.js';
12
+ import { quickCommandRegex } from '../../regex.js';
13
+ import { getUrlToStackSpotAI } from '../../utils/url.js';
14
+ const sections = [undefined, 'personal', 'workspace', 'account', 'shared'];
15
+ const CommandListItem = ({ qc, onSelect }) => {
16
+ const t = useTranslate(dictionary);
17
+ return (_jsxs("li", { children: [_jsxs("button", { className: "qc", onClick: () => onSelect(qc.slug),
18
+ // the following line prevents a new line character in the message when the user presses enter to select a qc.
19
+ onKeyDown: e => e.key === 'Enter' && e.preventDefault(), onFocus: e => e.target.closest('li')?.classList.add('focus'), onBlur: e => e.target.closest('li')?.classList.remove('focus'), children: [_jsxs("p", { className: "qc-title", children: ["/", qc.slug] }), _jsx("p", { className: "qc-description", children: qc.description })] }), _jsx(IconButton, { as: "a", title: t.openQC, "aria-label": t.openQC, href: `${getUrlToStackSpotAI()}/quick-command/${qc.slug}`, target: "_blank", children: _jsx(ExternalLink, {}) })] }));
20
+ };
21
+ const CommandList = ({ filter, visibility, onSelect }) => {
22
+ const t = useTranslate(dictionary);
23
+ const quickCommands = aiClient.quickCommands.useQuery({ order: 'a-to-z' });
24
+ let filtered = quickCommands;
25
+ if (visibility || filter) {
26
+ const lowerFilter = filter?.toLocaleLowerCase();
27
+ filtered = quickCommands.filter(qc => (!lowerFilter || qc.slug.toLocaleLowerCase().startsWith(lowerFilter)) && (!visibility || qc.visibility_level === visibility));
28
+ }
29
+ if (!quickCommands.length)
30
+ return _jsx(Text, { className: "empty", colorScheme: "light.700", children: t.noData });
31
+ if (!filtered.length)
32
+ return _jsx(Text, { className: "empty", colorScheme: "light.700", children: t.noResults });
33
+ return (_jsx("ul", { className: "command-list", children: filtered.map(qc => _jsx(CommandListItem, { qc: qc, onSelect: onSelect }, qc.id)) }));
34
+ };
35
+ const SelectorContent = ({ filter, onClose, inputRef }) => {
36
+ const t = useTranslate(dictionary);
37
+ const ref = useRef(null);
38
+ const chat = useCurrentChat();
39
+ const [visibility, setVisibility] = useState();
40
+ const onSelectQC = useCallback((slug) => {
41
+ const newValue = `/${slug}`;
42
+ chat.set('nextMessage', newValue);
43
+ onClose();
44
+ if (!inputRef.current)
45
+ return;
46
+ // the following line prevents bugs by setting the text area value before react gets the chance to.
47
+ inputRef.current.value = newValue;
48
+ inputRef.current.focus();
49
+ }, []);
50
+ useKeyboardControls({
51
+ querySelectors: '.tabs button, button.qc',
52
+ disableTabBehavior: true,
53
+ onPressEscape: onClose,
54
+ onPressArrowLeft: () => ref.current?.querySelector('.tabs button.active')?.focus(),
55
+ onPressArrowRight: () => ref.current?.querySelector('button.qc')?.focus(),
56
+ ref,
57
+ }, []);
58
+ function createSectionItem(action) {
59
+ return (_jsx("li", { children: _jsx("button", { className: visibility === action ? 'active' : '', onFocus: () => setVisibility(action), children: t[action || 'all'] }) }, action ?? 'all'));
60
+ }
61
+ return (_jsxs("div", { ref: ref, children: [_jsxs("header", { children: [_jsx(IconBox, { children: _jsx(QuickCommand, {}) }), _jsx(Text, { as: "h3", children: "QUICK COMMANDS" })] }), _jsxs("div", { className: "body", children: [_jsx("ul", { className: "tabs", children: sections.map(createSectionItem) }), _jsx(FallbackBoundary, { message: t.error, mini: true, children: _jsx(CommandList, { onSelect: onSelectQC, filter: filter, visibility: visibility }) })] })] }));
62
+ };
63
+ export const QuickCommandSelector = ({ inputRef }) => {
64
+ const value = useCurrentChatState('nextMessage') ?? '';
65
+ const filter = useMemo(() => value === '/' || quickCommandRegex.test(value) ? value.substring(1) : undefined, [value]);
66
+ const [isClosed, setClosed] = useState(false);
67
+ const selectorRef = useRef(null);
68
+ const shouldRender = filter !== undefined && !isClosed;
69
+ // Resets the closed state whenever the message input is cleared
70
+ useEffect(() => {
71
+ if (!value)
72
+ setClosed(false);
73
+ }, [value]);
74
+ // Creates the following behavior while the user types in the message input:
75
+ // auto-complete on tab; move focus to the qc panel on press up or down; and close the qc panel on esc.
76
+ useEffect(() => {
77
+ function getFirst() {
78
+ return selectorRef.current?.querySelector('.qc');
79
+ }
80
+ function onKeyDown(event) {
81
+ const key = event.key;
82
+ if (!selectorRef.current)
83
+ return;
84
+ if (key === 'Tab') {
85
+ getFirst()?.click();
86
+ event.preventDefault();
87
+ }
88
+ else if (key === 'ArrowDown' || key === 'ArrowUp') {
89
+ getFirst()?.focus();
90
+ event.preventDefault();
91
+ }
92
+ if (key === 'Escape') {
93
+ setClosed(true);
94
+ }
95
+ }
96
+ inputRef.current?.addEventListener('keydown', onKeyDown);
97
+ return () => inputRef.current?.removeEventListener('keydown', onKeyDown);
98
+ }, []);
99
+ // Closes the panel when the user clicks outside the qc panel or the message input.
100
+ useEffect(() => {
101
+ if (!shouldRender)
102
+ return;
103
+ function onClickOut(e) {
104
+ const target = e.target;
105
+ if (!selectorRef.current?.contains(target) && !inputRef.current?.contains(target))
106
+ setClosed(true);
107
+ }
108
+ document.addEventListener('click', onClickOut);
109
+ return () => document.removeEventListener('click', onClickOut);
110
+ }, [shouldRender]);
111
+ return (_jsx(Fading, { visible: shouldRender, ref: selectorRef, className: "quick-command-selector", children: _jsx(SelectorContent, { filter: filter, onClose: () => setClosed(true), inputRef: inputRef }) }));
112
+ };
113
+ const dictionary = {
114
+ en: {
115
+ all: 'All',
116
+ personal: 'Personal',
117
+ account: 'Account',
118
+ shared: 'Shared',
119
+ workspace: 'Workspace',
120
+ error: 'Could not load the quick commands.',
121
+ noData: 'You don\'t have any quick command yet.',
122
+ noResults: 'There are no quick commands to show here.',
123
+ openQC: 'Open this quick command\'s settings in a new tab.',
124
+ },
125
+ pt: {
126
+ all: 'Todos',
127
+ personal: 'Pessoal',
128
+ account: 'Conta',
129
+ shared: 'Compartilhado',
130
+ workspace: 'Workspace',
131
+ error: 'Não foi possível carregar os quick commands.',
132
+ noData: 'Você ainda não possui quick commands.',
133
+ noResults: 'Não há quick commands para mostrar aqui.',
134
+ openQC: 'Abra as configurações deste quick command em uma nova aba.',
135
+ },
136
+ };
137
+ //# sourceMappingURL=QuickCommandSelector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuickCommandSelector.js","sourceRoot":"","sources":["../../../src/views/MessageInput/QuickCommandSelector.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAOrD,UAAU,KAAK;IACb,QAAQ,EAAE,oBAAoB,CAAC;CAChC;AAED,eAAO,MAAM,YAAY,iBAAkB,KAAK,4CAgE/C,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AASrD,UAAU,KAAK;IACb,QAAQ,EAAE,oBAAoB,CAAC;CAChC;AAED,eAAO,MAAM,YAAY,iBAAkB,KAAK,4CAkE/C,CAAA"}
@@ -4,10 +4,12 @@ import { useCallback, useEffect, useRef, useState } from 'react';
4
4
  import { AdaptiveTextArea } from '../../components/AdaptiveTextArea.js';
5
5
  import { ProgressBar } from '../../components/ProgressBar.js';
6
6
  import { useCurrentChat, useCurrentChatState, useWidgetState } from '../../context/hooks.js';
7
+ import { quickCommandRegex } from '../../regex.js';
7
8
  import { ChatEntry } from '../../state/ChatEntry.js';
8
9
  import { ButtonGroup } from './ButtonGroup.js';
9
10
  import { useMessageInputDictionary } from './dictionary.js';
10
11
  import { InfoBar } from './InfoBar.js';
12
+ import { QuickCommandSelector } from './QuickCommandSelector.js';
11
13
  import { MAX_INPUT_HEIGHT, MessageInputBox, MIN_INPUT_HEIGHT } from './styled.js';
12
14
  export const MessageInput = ({ features }) => {
13
15
  const t = useMessageInputDictionary();
@@ -18,14 +20,14 @@ export const MessageInput = ({ features }) => {
18
20
  const isLoading = useCurrentChatState('isLoading') ?? false;
19
21
  const value = useCurrentChatState('nextMessage') ?? '';
20
22
  const isMinimized = useWidgetState('isMinimized');
21
- const elementRef = useRef(null);
23
+ const textAreaRef = useRef(null);
22
24
  const onSend = useCallback(async () => {
23
25
  const message = chat.get('nextMessage');
24
26
  if (!message)
25
27
  return;
26
28
  const code = chat.get('codeSelection');
27
29
  const language = chat.get('codeLanguage');
28
- const prompt = code && !message.startsWith('/') ? `${message}\n\`\`\`${language}\n${code}\n\`\`\`` : message;
30
+ const prompt = code && !quickCommandRegex.test(message) ? `${message}\n\`\`\`${language}\n${code}\n\`\`\`` : message;
29
31
  chat.pushMessage(ChatEntry.createUserEntry(prompt, true));
30
32
  chat.set('nextMessage', '');
31
33
  setFocused(false);
@@ -38,9 +40,9 @@ export const MessageInput = ({ features }) => {
38
40
  }, [onSend]);
39
41
  useEffect(() => {
40
42
  if (!isLoading)
41
- elementRef.current?.querySelector('textarea')?.focus();
43
+ textAreaRef.current?.focus();
42
44
  }, [isLoading]);
43
- return (_jsxs(MessageInputBox, { ref: elementRef, "aria-busy": isLoading, className: "message-input", children: [_jsx(ProgressBar, { visible: isLoading, shimmer: true }), _jsx(InfoBar, {}), _jsxs("div", { className: listToClass(['action-box', focused && 'focused', isLoading && 'disabled']), children: [_jsx(AdaptiveTextArea, { disabled: isLoading, placeholder: t.placeholder, onChange: e => chat.set('nextMessage', e.target.value), value: value, onFocus: () => setFocused(true), onBlur: () => setFocused(false), onKeyDown: onKeyDown, onIncreaseSize: () => setExpanded(false), onResetSize: () => !expansionLocked.current && setExpanded(true), maxHeight: isMinimized ? MIN_INPUT_HEIGHT : MAX_INPUT_HEIGHT }), _jsx(ButtonGroup, { features: features, onSend: onSend, onCancel: () => chat.abort(), expanded: expanded, isLoading: isLoading, setExpanded: (value) => {
45
+ return (_jsxs(MessageInputBox, { "aria-busy": isLoading, className: "message-input", children: [_jsx(ProgressBar, { visible: isLoading, shimmer: true }), _jsx(InfoBar, {}), _jsxs("div", { className: listToClass(['action-box', focused && 'focused', isLoading && 'disabled']), children: [_jsx(QuickCommandSelector, { inputRef: textAreaRef }), _jsx(AdaptiveTextArea, { ref: textAreaRef, disabled: isLoading, placeholder: t.placeholder, onChange: e => chat.set('nextMessage', e.target.value), value: value, onFocus: () => setFocused(true), onBlur: () => setFocused(false), onKeyDown: onKeyDown, onIncreaseSize: () => setExpanded(false), onResetSize: () => !expansionLocked.current && setExpanded(true), maxHeight: isMinimized ? MIN_INPUT_HEIGHT : MAX_INPUT_HEIGHT }), _jsx(ButtonGroup, { features: features, onSend: onSend, onCancel: () => chat.abort(), expanded: expanded, isLoading: isLoading, setExpanded: (value) => {
44
46
  setExpanded(value);
45
47
  expansionLocked.current = expanded;
46
48
  } })] })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":"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"}
@@ -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,wOAkM3B,CAAA"}
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,wOA2U3B,CAAA"}
@@ -80,6 +80,7 @@ export const MessageInputBox = styled.div `
80
80
 
81
81
  .action-box {
82
82
  display: flex;
83
+ position: relative;
83
84
  flex-direction: row;
84
85
  gap: 4px;
85
86
  align-items: end;
@@ -199,5 +200,141 @@ export const MessageInputBox = styled.div `
199
200
  box-shadow: none;
200
201
  }
201
202
  }
203
+
204
+ .quick-command-selector {
205
+ position: absolute;
206
+ border-radius: 4px;
207
+ border: 1px solid ${theme.color.light[600]};
208
+ background-color: ${theme.color.light[500]};
209
+ box-shadow: 0px 2px 16px 0px #0000005C;
210
+ display: flex;
211
+ flex-direction: column;
212
+ width: 480px;
213
+ bottom: 55px;
214
+
215
+ .loading, .error {
216
+ padding-bottom: 26px;
217
+ p {
218
+ width: 200px;
219
+ text-align: center;
220
+ line-height: 20px;
221
+ }
222
+ }
223
+
224
+ .empty {
225
+ padding-bottom: 26px;
226
+ width: 200px;
227
+ text-align: center;
228
+ line-height: 20px;
229
+ margin: auto;
230
+ }
231
+
232
+ header {
233
+ display: flex;
234
+ flex-direction: row;
235
+ gap: 8px;
236
+ align-items: center;
237
+ padding: 8px;
238
+ font-family: 'San Francisco';
239
+ font-weight: 500;
240
+ font-size: 11px;
241
+ }
242
+
243
+ .body {
244
+ display: flex;
245
+ flex-direction: row;
246
+ align-items: center;
247
+ }
248
+
249
+ ul {
250
+ margin: 0;
251
+ padding: 0;
252
+ list-style: none;
253
+ }
254
+
255
+ ul.tabs {
256
+ display: flex;
257
+ flex-direction: column;
258
+
259
+ li {
260
+ display: flex;
261
+ flex-direction: column;
262
+ }
263
+
264
+ button {
265
+ box-sizing: border-box;
266
+ color: ${theme.color.light[700]};
267
+ text-align: left;
268
+ padding: 10px;
269
+ font-weight: 600;
270
+ font-size: 12px;
271
+ transition: background-color 0.3s;
272
+ border-top-right-radius: 4px;
273
+ border-bottom-right-radius: 4px;
274
+ background-color: transparent;
275
+ border: none;
276
+ cursor: pointer;
277
+ outline: none;
278
+
279
+ &:hover, &.active, &:focus {
280
+ background-color: ${theme.color.light[600]};
281
+ }
282
+
283
+ &.active {
284
+ border-left: 1px solid ${theme.color.light.contrastText};
285
+ color: ${theme.color.light.contrastText};
286
+ }
287
+ }
288
+ }
289
+
290
+ ul.command-list {
291
+ align-self: stretch;
292
+ display: flex;
293
+ flex-direction: column;
294
+ gap: 2px;
295
+ overflow-y: auto;
296
+ flex: 1;
297
+ max-height: 170px;
298
+
299
+ li {
300
+ display: flex;
301
+ flex-direction: row;
302
+ align-items: center;
303
+ gap: 8px;
304
+ padding: 8px;
305
+ border-radius: 4px;
306
+
307
+ &:hover, &.focus {
308
+ background-color: ${theme.color.light[600]};
309
+ }
310
+
311
+ button.qc {
312
+ flex: 1;
313
+ border: none;
314
+ text-align: left;
315
+ background-color: transparent;
316
+ text-align: left;
317
+ outline: none;
318
+ overflow: hidden;
319
+ cursor: pointer;
320
+
321
+ .qc-title {
322
+ font-size: 11px;
323
+ margin: 0 0 4px 0;
324
+ color: ${theme.color.light.contrastText};
325
+ text-transform: uppercase;
326
+ text-overflow: ellipsis;
327
+ overflow: hidden;
328
+ }
329
+
330
+ .qc-description {
331
+ color: ${theme.color.light[700]};
332
+ font-size: 12px;
333
+ margin: 0;
334
+ }
335
+ }
336
+ }
337
+ }
338
+ }
202
339
  `;
203
340
  //# sourceMappingURL=styled.js.map
@@ -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,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;kBAWvB,eAAe,GAAG,qBAAqB;;;;;;;;;;;;;;;;gBAgBzC,eAAe;;0BAEL,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4BtC,UAAU;;;;;;;;;;;;;;;;;;;;;;wBAsBI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;wBACtB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;sBAKxB,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;;;;0BAIpB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;gBAoBhC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;4BAMV,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;4BAItB,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;;;;;kBAKlC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAmD5C,UAAU;;;;;;;;;;;cAWF,gBAAgB;;;;;;;;CAQ7B,CAAA"}
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,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;kBAWvB,eAAe,GAAG,qBAAqB;;;;;;;;;;;;;;;;gBAgBzC,eAAe;;0BAEL,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4BtC,UAAU;;;;;;;;;;;;;;;;;;;;;;;wBAuBI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;wBACtB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;sBAKxB,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;;;;0BAIpB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;gBAoBhC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;4BAMV,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;4BAItB,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;;;;;kBAKlC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAmD5C,UAAU;;;;;;;;;;;cAWF,gBAAgB;;;;;;;;;;;;wBAYN,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;wBACtB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0D7B,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;;;;;;;8BAcT,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;mCAIjB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY;mBAC9C,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY;;;;;;;;;;;;;;;;;;;;;;;8BAuBnB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;qBAgB/B,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY;;;;;;;qBAO9B,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;;;;;;;;CAQ1C,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stack-spot/ai-chat-widget",
3
- "version": "0.11.0",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,7 +8,7 @@
8
8
  "@citric/core": "^6.0.0",
9
9
  "@citric/icons": "^5.4.0 || ^6.0.0",
10
10
  "@citric/ui": "^5.4.0 || ^6.0.0",
11
- "@stack-spot/portal-components": "^2.6.1",
11
+ "@stack-spot/portal-components": "^2.8.0",
12
12
  "@stack-spot/portal-network": "^0.46.0",
13
13
  "@stack-spot/portal-theme": "^1.0.0",
14
14
  "@stack-spot/portal-translate": "^1.1.0",
@@ -4,6 +4,7 @@ import { Dictionary, interpolate, translate } from '@stack-spot/portal-translate
4
4
  import type { editor } from 'monaco-editor'
5
5
  import { ulid } from 'ulid'
6
6
  import { AbortedError } from '../AbortedError'
7
+ import { quickCommandRegex } from '../regex'
7
8
  import { ChatEntry } from '../state/ChatEntry'
8
9
  import { ChatState } from '../state/ChatState'
9
10
  import { LabeledWithImage } from '../state/types'
@@ -24,7 +25,6 @@ interface QCContext {
24
25
  signal: AbortSignal,
25
26
  }
26
27
 
27
- const commandRegex = /^\/[\w\d-_]+$/
28
28
  const progressMessageDelayMS = 1000
29
29
 
30
30
  export function createQuickCommandInterceptor(widget: WidgetState, getEditor: () => typeof editor | undefined) {
@@ -82,43 +82,43 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
82
82
  message.setValue({
83
83
  ...message.getValue(),
84
84
  content: interpolate(t.progress, qc.name || qc.slug, stepIndex + 1, qc.steps?.[stepIndex]?.slug, qc.steps?.length),
85
- updated: new Date().toISOString(),
86
85
  })
87
86
  }
88
87
 
89
- function showProgressMessage(ctx: QCContext, message: ChatEntry) {
90
- const result = {
91
- removeProgressMessage: () => clearTimeout(timeout),
92
- }
93
- const timeout = window.setTimeout(() => {
94
- const chat = ctx.chat
95
- chat.pushMessage(message)
96
- result.removeProgressMessage = () => chat.popMessage()
97
- }, progressMessageDelayMS)
98
- return result
99
- }
100
-
101
88
  function createProgressMessage(agent?: LabeledWithImage) {
102
89
  return new ChatEntry({
103
90
  agentType: 'bot',
104
91
  content: '',
105
92
  type: 'text',
106
93
  agent,
107
- updated: new Date().toISOString(),
108
94
  })
109
95
  }
110
96
 
97
+ function showProgressMessage(ctx: QCContext) {
98
+ const showImmediately = ctx.qc.steps?.some(s => s.type === 'LLM')
99
+ const message = createProgressMessage(ctx.chat.get('agent'))
100
+ const controller = {
101
+ update: (index: number) => updateProgressMessageForStep(message, ctx.qc, index),
102
+ remove: () => clearTimeout(timeout),
103
+ }
104
+ const timeout = window.setTimeout(() => {
105
+ const chat = ctx.chat
106
+ chat.pushMessage(message)
107
+ controller.remove = () => chat.popMessage()
108
+ }, showImmediately ? 0 : progressMessageDelayMS)
109
+ return controller
110
+ }
111
+
111
112
  async function runSteps(ctx: QCContext) {
112
- const { qc, chat } = ctx
113
- const progress = createProgressMessage(chat.get('agent'))
114
- const { removeProgressMessage } = showProgressMessage(ctx, progress)
113
+ const { qc } = ctx
114
+ const progress = showProgressMessage(ctx)
115
115
  try {
116
116
  for (let i = 0; i < (qc.steps?.length ?? 0); i++) {
117
- updateProgressMessageForStep(progress, qc, i)
117
+ progress.update(i)
118
118
  await (qc.steps?.[i].type === 'FETCH' ? runFetchStep(ctx, i) : runLLMStep(ctx, i))
119
119
  }
120
120
  } finally {
121
- removeProgressMessage()
121
+ progress.remove()
122
122
  }
123
123
  }
124
124
 
@@ -204,6 +204,7 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
204
204
  content: result,
205
205
  agent: chat.get('agent'),
206
206
  type: 'md',
207
+ updated: new Date().toISOString(),
207
208
  }))
208
209
  } else {
209
210
  const editor = getEditor()?.getEditors()[0]
@@ -217,7 +218,7 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
217
218
 
218
219
  async function quickCommandInterceptor(entry: ChatEntry, chat: ChatState, signal: AbortSignal) {
219
220
  const { agentType, content } = entry.getValue()
220
- if (agentType !== 'user' || !commandRegex.test(content.trim())) return
221
+ if (agentType !== 'user' || !quickCommandRegex.test(content.trim())) return
221
222
  const t = translate(dictionary)
222
223
  const slug = content.trim().substring(1)
223
224
  const ctx: QCContext = {
@@ -258,14 +259,14 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
258
259
 
259
260
  const dictionary = {
260
261
  en: {
261
- requiresSelection: 'This quick command requires some code to be selected in the editor.',
262
+ requiresSelection: 'This quick command requires some code to be selected in the editor. To open the editor click the icon "{/}" in the field below.',
262
263
  startQuestioning: 'To execute the Quick Command "$0", I\'ll need you to provide some information. Some may be mandatory, and others optional. Let\'s get started.',
263
264
  progress: 'Running quick command "$0". Step $1 ($2) of $3.',
264
265
  aborted: 'The quick command execution aborted by the user.',
265
266
  notFound: 'There\'s no quick command with the provided name. If you don\'t wish to run a command, prefix the first "/" with a "\\".',
266
267
  },
267
268
  pt: {
268
- requiresSelection: 'Este quick command precisa que algum código esteja selecionado no editor.',
269
+ requiresSelection: 'Este quick command precisa que algum código esteja selecionado no editor. Para abrir o editor clique no ícone "{/}" no campo abaixo.',
269
270
  startQuestioning: 'Para executar o Quick Command "$0", vou precisar que você providencie algumas explicações. Algumas são obrigatórias e outras opcionais. Vamos começar.',
270
271
  progress: 'Executando quick command "$0". Passo $1 ($2) de $3.',
271
272
  aborted: 'A execução do quick command foi abortada pelo usuário.',
@@ -1,9 +1,28 @@
1
1
  import { aiClient, StackspotAPIError } from '@stack-spot/portal-network'
2
- import { ChatEntry, KnowledgeSource } from '../state/ChatEntry'
2
+ import { ChatResponse3 } from '@stack-spot/portal-network/api/ai'
3
+ import { ChatEntry, KnowledgeSource, TextChatEntry } from '../state/ChatEntry'
3
4
  import { ChatState } from '../state/ChatState'
5
+ import { LabeledWithImage } from '../state/types'
4
6
  import { buildConversationContext } from '../utils/chat'
5
7
  import { genericSourcesToKnowledgeSources } from '../utils/knowledge-source'
6
8
 
9
+ function createEntryValueFromChatResponse(
10
+ response: Partial<ChatResponse3>,
11
+ knowledgeSources: KnowledgeSource[] | undefined,
12
+ agent: LabeledWithImage | undefined,
13
+ includeDate = false,
14
+ ): TextChatEntry {
15
+ return {
16
+ agentType: 'bot',
17
+ type: 'md',
18
+ content: response.answer ?? '',
19
+ messageId: response.message_id ?? undefined,
20
+ knowledgeSources,
21
+ agent: agent,
22
+ updated: includeDate ? new Date().toISOString() : undefined,
23
+ }
24
+ }
25
+
7
26
  export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState, signal: AbortSignal) {
8
27
  const { agentType, content } = entry.getValue()
9
28
  if (agentType !== 'user') return
@@ -20,18 +39,11 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState,
20
39
  if (value.sources?.length !== knowledgeSources?.length) {
21
40
  knowledgeSources = genericSourcesToKnowledgeSources(value.sources)
22
41
  }
23
- botEntry.setValue({
24
- agentType: 'bot',
25
- type: 'md',
26
- content: value.answer ?? '',
27
- messageId: value.message_id ?? undefined,
28
- knowledgeSources,
29
- updated: new Date().toISOString(),
30
- agent: chat.get('agent'),
31
- })
42
+ botEntry.setValue(createEntryValueFromChatResponse(value, knowledgeSources, chat.get('agent')))
32
43
  })
33
44
  try {
34
- await stream.getValue()
45
+ const finalValue = await stream.getValue()
46
+ botEntry.setValue(createEntryValueFromChatResponse(finalValue, knowledgeSources, chat.get('agent'), true))
35
47
  aiClient.chat.invalidate({ conversationId: chat.id })
36
48
  if (isFirstMessage) aiClient.chats.invalidate()
37
49
  } catch (error: any) {
@@ -1,5 +1,6 @@
1
+ /* eslint-disable react/display-name */
1
2
  import { Textarea } from '@citric/core'
2
- import { useEffect, useRef } from 'react'
3
+ import { forwardRef, useEffect, useRef } from 'react'
3
4
  import { PropsOf } from '../types'
4
5
 
5
6
  interface Props extends PropsOf<typeof Textarea> {
@@ -8,8 +9,12 @@ interface Props extends PropsOf<typeof Textarea> {
8
9
  maxHeight?: number,
9
10
  }
10
11
 
11
- export const AdaptiveTextArea = ({ value, onIncreaseSize, onResetSize, maxHeight, style, ...props }: Props) => {
12
- const ref = useRef<HTMLTextAreaElement>(null)
12
+ export const AdaptiveTextArea = forwardRef<HTMLTextAreaElement, Props>((
13
+ { value, onIncreaseSize, onResetSize, maxHeight, style, ...props },
14
+ externalRef,
15
+ ) => {
16
+ const localRef = useRef<HTMLTextAreaElement>(null)
17
+ const ref = externalRef as React.RefObject<HTMLTextAreaElement> ?? localRef
13
18
 
14
19
  useEffect(() => {
15
20
  if (!ref.current) return
@@ -31,4 +36,4 @@ export const AdaptiveTextArea = ({ value, onIncreaseSize, onResetSize, maxHeight
31
36
  }, [value, maxHeight])
32
37
 
33
38
  return <Textarea {...props} ref={ref} value={value} style={{ ...style, maxHeight }} />
34
- }
39
+ })
@@ -0,0 +1,20 @@
1
+ /* eslint-disable react/display-name */
2
+ import { forwardRef, RefObject, useEffect, useRef } from 'react'
3
+
4
+ interface Props extends React.HTMLAttributes<HTMLDivElement> {
5
+ delay?: number,
6
+ }
7
+
8
+ export const AutoFocus = forwardRef<HTMLDivElement, Props>(({ children, delay = 0, ...props }, externalRef) => {
9
+ const localRef = useRef<HTMLDivElement>(null)
10
+ const ref = externalRef as RefObject<HTMLDivElement> ?? localRef
11
+
12
+ useEffect(() => {
13
+ setTimeout(() => {
14
+ // fixme: we should actually call `focusFirstChild` from the component lib, but, it's bugged
15
+ (ref.current?.querySelector('a, button, input, select, textarea') as HTMLElement)?.focus()
16
+ }, delay)
17
+ }, [])
18
+
19
+ return <div ref={ref} {...props}>{children}</div>
20
+ })