Files
twenty/packages/twenty-front/src/modules/ai/components/AIChatEditorSection.tsx
T

166 lines
5.4 KiB
TypeScript

import { styled } from '@linaria/react';
import { EditorContent } from '@tiptap/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { AIChatEmptyState } from '@/ai/components/AIChatEmptyState';
import { AIChatStandaloneError } from '@/ai/components/AIChatStandaloneError';
import { AgentChatContextPreview } from '@/ai/components/internal/AgentChatContextPreview';
import { AgentChatFileUploadButton } from '@/ai/components/internal/AgentChatFileUploadButton';
import { AIChatContextUsageButton } from '@/ai/components/internal/AIChatContextUsageButton';
import { AIChatEditorFocusEffect } from '@/ai/components/internal/AIChatEditorFocusEffect';
import { AIChatSkeletonLoader } from '@/ai/components/internal/AIChatSkeletonLoader';
import { SendMessageButton } from '@/ai/components/internal/SendMessageButton';
import { useAIChatEditor } from '@/ai/hooks/useAIChatEditor';
import { useAiModelOptions } from '@/ai/hooks/useAiModelOptions';
import { useAgentChatModelId } from '@/ai/hooks/useAgentChatModelId';
import { agentChatUserSelectedModelState } from '@/ai/states/agentChatUserSelectedModelState';
import { Select } from '@/ui/input/components/Select';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { type SelectOption } from 'twenty-ui/input';
const StyledInputArea = styled.div<{ isMobile: boolean }>`
align-items: flex-end;
background: ${themeCssVariables.background.primary};
display: flex;
flex-direction: column;
flex-shrink: 0;
gap: ${themeCssVariables.spacing[2]};
padding-block: ${({ isMobile }) =>
isMobile ? '0' : themeCssVariables.spacing[3]};
padding-inline: ${themeCssVariables.spacing[3]};
`;
const StyledInputBox = styled.div`
background-color: ${themeCssVariables.background.transparent.lighter};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${themeCssVariables.border.radius.sm};
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: ${themeCssVariables.spacing[2]};
min-height: 140px;
padding: ${themeCssVariables.spacing[2]};
width: 100%;
&:focus-within {
border-color: ${themeCssVariables.color.blue};
box-shadow: 0px 0px 0px 3px ${themeCssVariables.color.transparent.blue2};
}
`;
const StyledEditorWrapper = styled.div`
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
.tiptap {
background: transparent;
border: none;
box-shadow: none;
color: ${themeCssVariables.font.color.primary};
font-family: inherit;
font-size: ${themeCssVariables.font.size.md};
font-weight: ${themeCssVariables.font.weight.regular};
line-height: 16px;
max-height: 320px;
min-height: 48px;
outline: none;
overflow-y: auto;
padding: 0;
p {
margin: 0;
}
p.is-editor-empty:first-of-type::before {
color: ${themeCssVariables.font.color.light};
content: attr(data-placeholder);
float: left;
font-weight: ${themeCssVariables.font.weight.regular};
height: 0;
pointer-events: none;
}
}
`;
const StyledButtonsContainer = styled.div`
align-items: center;
display: flex;
justify-content: space-between;
width: 100%;
`;
const StyledLeftButtonsContainer = styled.div`
align-items: center;
display: flex;
gap: ${themeCssVariables.spacing['0.5']};
`;
const StyledRightButtonsContainer = styled.div`
align-items: center;
display: flex;
gap: ${themeCssVariables.spacing[1]};
`;
export const AIChatEditorSection = () => {
const isMobile = useIsMobile();
const { options, pinnedOption } = useAiModelOptions({
variant: 'pinned-default',
});
const smartModelOptions: SelectOption<string | null>[] = options;
const defaultPinnedOption: SelectOption<string | null> | undefined =
pinnedOption
? {
...pinnedOption,
value: null,
}
: undefined;
const setAgentChatUserSelectedModel = useSetAtomState(
agentChatUserSelectedModelState,
);
const { selectedModelId } = useAgentChatModelId();
const { editor, handleSendAndClear } = useAIChatEditor();
return (
<>
<AIChatEditorFocusEffect editor={editor} />
<AIChatEmptyState editor={editor} />
<AIChatStandaloneError />
<AIChatSkeletonLoader />
<StyledInputArea isMobile={isMobile}>
<AgentChatContextPreview />
<StyledInputBox>
<StyledEditorWrapper>
<EditorContent editor={editor} />
</StyledEditorWrapper>
<StyledButtonsContainer>
<StyledLeftButtonsContainer>
<AgentChatFileUploadButton />
<AIChatContextUsageButton />
</StyledLeftButtonsContainer>
<StyledRightButtonsContainer>
<Select
dropdownId="ai-chat-smart-model-select"
value={selectedModelId}
onChange={setAgentChatUserSelectedModel}
options={smartModelOptions}
pinnedOption={defaultPinnedOption}
selectSizeVariant="small"
showContextualTextInControl={false}
withSearchInput
dropdownOffset={{ x: 0, y: 8 }}
/>
<SendMessageButton onSend={handleSendAndClear} />
</StyledRightButtonsContainer>
</StyledButtonsContainer>
</StyledInputBox>
</StyledInputArea>
</>
);
};