diff --git a/src/components/Assistant/Assistant.styl b/src/components/Assistant/Assistant.styl new file mode 100644 index 000000000..af98932f4 --- /dev/null +++ b/src/components/Assistant/Assistant.styl @@ -0,0 +1,123 @@ +.assistant + display flex + flex-wrap wrap + padding 16px + + &__history + display flex + flex-wrap wrap + width 100% + cursor pointer + margin-top 10px + + &-item + width 100% + margin 2px 0 + padding 1px 5px + display: flex; + justify-content: space-between; + align-items center + + &-delete + width 8px + height 8px + display flex + opacity .5 + + &:hover + opacity 1 + + &:hover + background-color #F5F5F5 + + + .assist-form + --primary-action-color #00A8FF + --primary-action-padding 4px + --primary-action-border-radius 4px + --primary-action-surface-color-hover rgba(9, 109, 217, 0.12) + --text-input-min-height 40px + --tooltip-text-color #F5222D + + display grid + grid-template-columns: auto var(--text-input-min-height); + align-items center + gap 4px + width 100% + + &_disabled + opacity .5 !important + pointer-events none + + &_loading + --borderWidth: 3px; + background: #FFF; + position: relative; + border-radius 4px + pointer-events: none; + + &:after + content: ''; + position: absolute; + top: calc(-1 * var(--borderWidth)); + left: calc(-1 * var(--borderWidth)); + height: calc(100% + var(--borderWidth) * 2); + width: calc(100% + var(--borderWidth) * 2); + background: linear-gradient(60deg, #9254DE, #9254DE, #13C2C2, #13C2C2); + border-radius: calc(2 * var(--borderWidth)); + z-index: -1; + animation: animatedgradient 3s ease alternate infinite; + background-size: 300% 300%; + + &__primary-action + height 100% + width 100% + display flex + justify-content center + align-items flex-start + flex-shrink 0 + flex-grow 0 + grid-row 1 / 2 + grid-column 2 / -1 + + &_loading + filter grayscale(1) + opacity .6 + + button + margin-top 4px + display flex + justify-content center + align-items center + flex-shrink 0 + flex-grow 0 + appearance none + border none + background-color transparent + color var(--primary-action-color) + border-radius var(--primary-action-border-radius) + padding 0 + height calc(var(--text-input-min-height) - 8px); + width calc(var(--text-input-min-height) - 8px); + + &:hover + background-color var(--primary-action-surface-color-hover) + + &__tooltipMessage + color var(--tooltip-text-color) + font-size 0.9em + + +@keyframes animatedgradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + + diff --git a/src/components/Assistant/Assistant.tsx b/src/components/Assistant/Assistant.tsx new file mode 100644 index 000000000..30ec8d834 --- /dev/null +++ b/src/components/Assistant/Assistant.tsx @@ -0,0 +1,117 @@ +import { FC, useCallback, useEffect, useState } from 'react'; +import { observer } from 'mobx-react'; +import { Block, Elem } from '../../utils/bem'; +import { ReactComponent as IconSend } from '../../assets/icons/send.svg'; +import { IconCross } from '../../assets/icons'; +import { SpinnerCircle } from '../SpinnerCircle/SpinnerCircle'; + +import './Assistant.styl'; +import { TextArea } from '../../common/TextArea/TextArea'; + +const MAX_NUMBER_OF_HISTORY_ITEMS = 10; + +export const Assistant: FC<{ onPrompt: (prompt: string) => void, awaitingSuggestions: boolean }> = observer(({ onPrompt, awaitingSuggestions }) => { + const [historyValue, setHistoryValue] = useState([]); + const [value, setValue] = useState(''); + + useEffect(() => { + const _history = JSON.parse(window.localStorage.getItem('llm_assistant') || '[]'); + + if (_history.length) { + setHistoryValue(_history); + setValue(historyValue[0]); + } + }, []); + + useEffect(() => { + window.localStorage.setItem('llm_assistant', JSON.stringify(historyValue)); + }, [historyValue]); + + const setHistory = useCallback((text: string) => { + const _history = [...historyValue]; + + _history.forEach((item: string, index: number) => { + if (item === text) { + _history.splice(index, 1); + } + }); + + _history.unshift(text); + + if (_history.length > MAX_NUMBER_OF_HISTORY_ITEMS) { + _history.pop(); + } + + setHistoryValue(_history); + }, [historyValue]); + + + const onSubmit = useCallback((e) => { + e?.preventDefault?.(); + + if (!value.trim()) return; + + onPrompt(value); + + setHistory(value); + }, [value]); + + const setValueFromHistory = useCallback((item: string) => { + setValue(item); + onPrompt(item); + setHistory(item); + }, [historyValue]); + + const deleteValueFromHistory = useCallback((deleteItem: string) => { + const _history = [...historyValue]; + + _history.forEach((item: string, index: number) => { + if (item === deleteItem) { + _history.splice(index, 1); + } + }); + + setHistoryValue(_history); + }, [historyValue]); + + const renderHistory = () => { + return historyValue.map((item: string, index: number) => { + return ( + setValueFromHistory(item)}> + {item} + { + e.stopPropagation(); + deleteValueFromHistory(item); + }}> + + + + ); + }); + }; + + return ( + + +