Skip to content
This repository has been archived by the owner on Apr 18, 2024. It is now read-only.

Test assistant prompt #1643

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
123 changes: 123 additions & 0 deletions src/components/Assistant/Assistant.styl
Original file line number Diff line number Diff line change
@@ -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%;
}
}


117 changes: 117 additions & 0 deletions src/components/Assistant/Assistant.tsx
Original file line number Diff line number Diff line change
@@ -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<string[]>([]);
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 (
<Elem tag="div" name="history-item" key={index} onClick={() => setValueFromHistory(item)}>
{item}
<Elem tag="div" name="history-item-delete" onClick={(e:MouseEvent) => {
e.stopPropagation();
deleteValueFromHistory(item);
}}>
<IconCross />
</Elem>
</Elem>
);
});
};

return (
<Block name="assistant">
<Block tag="form" name="assist-form" mod={{ inline: true, loading: awaitingSuggestions }} onSubmit={onSubmit}>
<TextArea
className={awaitingSuggestions ? 'lsf-assist-form_disabled' : ''}
name="assist-text"
placeholder="Type your message here"
value={value}
onChange={setValue}
onSubmit={onSubmit}
/>
<Elem tag="div" name="primary-action"
mod={{ loading: awaitingSuggestions }}
>
<button type="submit">
<Elem name='icon' tag={awaitingSuggestions ? SpinnerCircle : IconSend} width={24} height={24}/>
</button>
</Elem>
</Block>
<Elem tag="div" name="history">
{historyValue.length > 0 && renderHistory()}
</Elem>
</Block>
);
});
15 changes: 15 additions & 0 deletions src/components/SidePanels/DetailsPanel/DetailsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { Relations as RelationsComponent } from './Relations';
// eslint-disable-next-line
// @ts-ignore
import { DraftPanel } from '../../DraftPanel/DraftPanel';
import { Assistant } from '../../Assistant/Assistant';

interface DetailsPanelProps extends PanelProps {
regions: any;
selection: any;
Expand Down Expand Up @@ -70,6 +72,18 @@ const CommentsTab: FC<any> = inject('store')(observer(({ store }) => {
);
}));

const AssistTab: FC<any> = inject('store')(observer(({ store }) => {
return (
<Block name={'assistant-panel'}>
<Elem name="section-tab">
<Elem name="section-content">
<Assistant onPrompt={store.onAssistantPrompt} awaitingSuggestions={store.awaitingSuggestions} />
</Elem>
</Elem>
</Block>
);
}));

const RelationsTab: FC<any> = inject('store')(observer(({ currentEntity }) => {
const { relationStore } = currentEntity;

Expand Down Expand Up @@ -207,6 +221,7 @@ const SelectedRegion: FC<{region: any}> = observer(({
);
});

export const Assist = observer(AssistTab);
export const Comments = observer(CommentsTab);
export const History = observer(HistoryTab);
export const Relations = observer(RelationsTab);
Expand Down
36 changes: 25 additions & 11 deletions src/components/SidePanels/TabPanels/utils.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { FC, MutableRefObject, ReactNode } from 'react';
import { clamp } from '../../../utils/utilities';
import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_MAX_HEIGHT, DEFAULT_PANEL_MAX_WIDTH, DEFAULT_PANEL_MIN_HEIGHT, DEFAULT_PANEL_WIDTH, PANEL_HEADER_HEIGHT } from '../constants';
import { Comments, History, Info, Relations } from '../DetailsPanel/DetailsPanel';
import {
DEFAULT_PANEL_HEIGHT,
DEFAULT_PANEL_MAX_HEIGHT,
DEFAULT_PANEL_MAX_WIDTH,
DEFAULT_PANEL_MIN_HEIGHT,
DEFAULT_PANEL_WIDTH,
PANEL_HEADER_HEIGHT
} from '../constants';
import { Assist, Comments, History, Info, Relations } from '../DetailsPanel/DetailsPanel';
import { OutlinerComponent } from '../OutlinerPanel/OutlinerPanel';
import { PanelProps } from '../PanelBase';
import { emptyPanel, JoinOrder, PanelBBox, PanelView, Side, ViewportSize } from './types';

export const determineLeftOrRight = (event: any, droppableElement?: ReactNode) => {
const element = droppableElement || event.target as HTMLElement;
const element = droppableElement || event.target as HTMLElement;
const dropWidth = (element as HTMLElement).clientWidth as number;
const x = event.pageX as number - (element as HTMLElement).getBoundingClientRect().left;
const half = dropWidth / 2;

return x > half ? Side.right : Side.left;
};

export const determineDroppableArea = (droppingElement: HTMLElement) => droppingElement?.id?.includes('droppable');

export const stateRemovedTab = (state: Record<string, PanelBBox>, movingPanel: string, movingTab: number) => {
Expand Down Expand Up @@ -49,7 +56,7 @@ export const setActiveDefaults = (state: Record<string, PanelBBox>) => {

Object.keys(state).forEach((panelKey: string) => {
const firstActiveTab = newState[panelKey].panelViews.findIndex((view) => view.active) ;

newState[panelKey].panelViews[firstActiveTab > 0 ? firstActiveTab : 0].active = true;
});

Expand Down Expand Up @@ -80,15 +87,15 @@ export const stateAddedTab = (
) => {
const newState = { ...state };
const panel = newState[receivingPanel];

panel.panelViews = newState[receivingPanel].panelViews.map((view) => {
view.active = false;
return view;
});

let index = receivingTab + (dropSide === Side.right ? 1 : 0);

if (movingPanel === receivingPanel && index > 0) index -= 1;
if (movingPanel === receivingPanel && index > 0) index -= 1;
panel.panelViews.splice(index, 0, movingTabData);
return newState;
};
Expand All @@ -107,6 +114,7 @@ export const panelComponents: {[key:string]: FC<PanelProps>} = {
'history': History as FC<PanelProps>,
'relations': Relations as FC<PanelProps>,
'comments': Comments as FC<PanelProps>,
'assist': Assist as FC<PanelProps>,
'info': Info as FC<PanelProps>,
};

Expand Down Expand Up @@ -142,6 +150,12 @@ const panelViews = [
component: panelComponents['comments'] as FC<PanelProps>,
active: false,
},
{
name: 'assist',
title: 'Assistant',
component: panelComponents['assist'] as FC<PanelProps>,
active: false,
},
];

export const enterprisePanelDefault: Record<string, PanelBBox> = {
Expand Down Expand Up @@ -173,7 +187,7 @@ export const enterprisePanelDefault: Record<string, PanelBBox> = {
detached: false,
alignment: Side.right,
maxHeight: DEFAULT_PANEL_MAX_HEIGHT,
panelViews: [panelViews[0], panelViews[2]],
panelViews: [panelViews[0], panelViews[2], panelViews[5]],
},
};

Expand All @@ -191,7 +205,7 @@ export const openSourcePanelDefault: Record<string, PanelBBox> = {
detached: false,
alignment: Side.right,
maxHeight: DEFAULT_PANEL_MAX_HEIGHT,
panelViews: [panelViews[3], panelViews[1]],
panelViews: [panelViews[3], panelViews[4], panelViews[1]],
},
'regions-relations': {
order: 2,
Expand Down
15 changes: 15 additions & 0 deletions src/components/SpinnerCircle/SpinnerCircle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import './SpinnerCircle.styl';
import { Block } from '../../utils/bem';

export const SpinnerCircle = ({ size, color, ...rest }) => {
const customStyles = {};

if (typeof size !== 'undefined') {
customStyles["----spinner-size"] = `${size}px`;
}
if (typeof color !== 'undefined') {
customStyles["----spinner-color"] = color;
}
return <Block name="circular-spinner" style={customStyles} {...rest}></Block>;
};
Loading
Loading