Skip to content

Commit

Permalink
studio conversation
Browse files Browse the repository at this point in the history
  • Loading branch information
anastasiya1155 committed Jan 17, 2024
1 parent bcdafe7 commit fce208d
Show file tree
Hide file tree
Showing 44 changed files with 1,913 additions and 2,254 deletions.
39 changes: 26 additions & 13 deletions client/src/Project/CurrentTabContent/StudioTab/ActionsDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@ import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import DropdownSection from '../../../components/Dropdown/Section';
import SectionItem from '../../../components/Dropdown/Section/SectionItem';
import { SplitViewIcon, TrashCanIcon } from '../../../icons';
import { deleteConversation } from '../../../services/api';
import { BroomIcon, SplitViewIcon, TrashCanIcon } from '../../../icons';
import { deleteCodeStudio, deleteConversation } from '../../../services/api';

type Props = {
clearConversation: () => void;
handleMoveToAnotherSide: () => void;
refreshCurrentProjectConversations: () => void;
refreshCurrentProjectStudios: () => void;
closeTab: (tabKey: string, side: 'left' | 'right') => void;
conversationId?: string;
studioId?: string;
projectId?: string;
tabKey: string;
side: 'left' | 'right';
};

const ActionsDropdown = ({
handleMoveToAnotherSide,
refreshCurrentProjectConversations,
conversationId,
refreshCurrentProjectStudios,
clearConversation,
studioId,
projectId,
closeTab,
tabKey,
Expand All @@ -27,16 +29,16 @@ const ActionsDropdown = ({
const { t } = useTranslation();

const removeConversation = useCallback(async () => {
if (projectId && conversationId) {
await deleteConversation(projectId, conversationId);
refreshCurrentProjectConversations();
if (projectId && studioId) {
await deleteCodeStudio(projectId, studioId);
refreshCurrentProjectStudios();
closeTab(tabKey, side);
}
}, [
projectId,
conversationId,
studioId,
closeTab,
refreshCurrentProjectConversations,
refreshCurrentProjectStudios,
tabKey,
side,
]);
Expand All @@ -49,15 +51,26 @@ const ActionsDropdown = ({

return (
<div>
<DropdownSection>
<DropdownSection borderBottom>
<SectionItem
label={t('Open in split view')}
shortcut={shortcuts.splitView}
onClick={handleMoveToAnotherSide}
// isFocused
icon={<SplitViewIcon sizeClassName="w-4 h-4" />}
/>
{conversationId && (
{studioId && (
<SectionItem
label={t('Clear conversation')}
// shortcut={shortcuts.splitView}
onClick={clearConversation}
// isFocused
icon={<BroomIcon sizeClassName="w-4 h-4" />}
/>
)}
</DropdownSection>
<DropdownSection>
{studioId && (
<SectionItem
label={t('Delete conversation')}
// shortcut={shortcuts.splitView}
Expand Down
182 changes: 119 additions & 63 deletions client/src/Project/CurrentTabContent/StudioTab/Conversation.tsx
Original file line number Diff line number Diff line change
@@ -1,85 +1,141 @@
import React, {
memo,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { ChatMessageServer } from '../../../types/general';
import { ProjectContext } from '../../../context/projectContext';
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
import { Trans } from 'react-i18next';
import ScrollToBottom from '../../../components/ScrollToBottom';
import { StudioContext, StudiosContext } from '../../../context/studiosContext';
import Input from './Input';
import ScrollableContent from './ScrollableContent';
import DeprecatedClientModal from './DeprecatedClientModal';
import { StudioContext } from '../../../context/studiosContext';
import { TOKEN_LIMIT } from '../../../consts/codeStudio';
import { BranchIcon, WarningSignIcon } from '../../../icons';
import SpinLoaderContainer from '../../../components/Loaders/SpinnerLoader';
import { StudioConversationMessageAuthor } from '../../../types/general';
import { getTemplates } from '../../../services/api';
import { StudioTemplateType } from '../../../types/api';
import StarterMessage from './StarterMessage';
import ConversationInput from './Input';
import GeneratedDiff from './GeneratedDiff';

type Props = {
side: 'left' | 'right';
tabKey: string;
studioData: StudioContext;
isActiveTab: boolean;
requestsLeft: number;
};

const Conversation = ({ side, tabKey }: Props) => {
const { project } = useContext(ProjectContext.Current);
const { studios } = useContext(StudiosContext);
const Conversation = ({
side,
studioData,
isActiveTab,
requestsLeft,
}: Props) => {
// const { project } = useContext(ProjectContext.Current);
const scrollableRef = useRef<HTMLDivElement>(null);
const [isScrollable, setIsScrollable] = useState(false);
const inputRef = useRef<HTMLTextAreaElement>(null);
const [templates, setTemplates] = useState<StudioTemplateType[]>([]);

const studioData: StudioContext | undefined = useMemo(
() => studios[tabKey],
[studios, tabKey],
);
const refetchTemplates = useCallback(() => {
getTemplates().then(setTemplates);
}, []);

useEffect(() => {
setTimeout(() => {
if (scrollableRef.current) {
setIsScrollable(
scrollableRef.current.scrollHeight >
scrollableRef.current.clientHeight,
);
}
}, 100);
}, [studioData?.conversation, studioData?.hideMessagesFrom]);
refetchTemplates();
}, []);
// const [isScrollable, setIsScrollable] = useState(false);

// useEffect(() => {
// setTimeout(() => {
// if (scrollableRef.current) {
// setIsScrollable(
// scrollableRef.current.scrollHeight >
// scrollableRef.current.clientHeight,
// );
// }
// }, 100);
// }, [studioData?.conversation, studioData?.hideMessagesFrom]);

return !studioData ? null : (
<div className="w-full max-w-2xl mx-auto flex flex-col flex-1 overflow-auto">
<ScrollToBottom
className="max-w-full flex flex-col overflow-auto"
wrapperRef={scrollableRef}
>
<ScrollableContent
studioData={studioData}
side={side}
projectId={project?.id!}
/>
<StarterMessage />
{studioData.conversation.map((m, i) => (
<ConversationInput
key={i}
author={m.author}
message={m.error || m.message}
onMessageChange={studioData.onMessageChange}
onMessageRemoved={studioData.onMessageRemoved}
i={i}
isTokenLimitExceeded={studioData.tokenCount > TOKEN_LIMIT}
isLast={i === studioData.conversation.length - 1}
side={side}
isActiveTab={isActiveTab}
requestsLeft={requestsLeft}
isLoading={studioData.isLoading}
handleCancel={studioData.handleCancel}
templates={templates}
/>
))}
{!!studioData.diff && (
<GeneratedDiff
diff={studioData.diff}
onDiffRemoved={studioData.onDiffRemoved}
onDiffChanged={studioData.onDiffChanged}
applyError={studioData.isDiffApplyError}
/>
)}
{(studioData.isDiffApplied ||
studioData.waitingForDiff ||
studioData.isDiffGenFailed) && (
<div
className={`w-full flex items-center rounded-6 justify-center gap-1 py-2 ${
studioData.isDiffGenFailed
? 'bg-bg-danger/12 text-bg-danger'
: 'bg-bg-main/12 text-label-link'
} caption`}
>
{studioData.isDiffGenFailed ? (
<WarningSignIcon raw sizeClassName="w-3.5 h-3.5" />
) : studioData.waitingForDiff ? (
<SpinLoaderContainer sizeClassName="w-3.5 h-3.5" />
) : (
<BranchIcon raw sizeClassName="w-3.5 h-3.5" />
)}
<Trans>
{studioData.isDiffGenFailed
? 'Diff generation failed'
: studioData.waitingForDiff
? 'Generating diff...'
: 'The diff has been applied locally.'}
</Trans>
</div>
)}
{!studioData.isLoading &&
// !studioData.isPreviewing &&
!studioData.waitingForDiff &&
!studioData.diff &&
!(
studioData.conversation[studioData.conversation.length - 1]
?.author === StudioConversationMessageAuthor.USER
) && (
<ConversationInput
key={'new'}
author={studioData.inputAuthor}
message={studioData.inputValue}
onMessageChange={studioData.onMessageChange}
inputRef={inputRef}
isTokenLimitExceeded={studioData.tokenCount > TOKEN_LIMIT}
isLast
side={side}
onSubmit={studioData.onSubmit}
isActiveTab={isActiveTab}
requestsLeft={requestsLeft}
isLoading={studioData.isLoading}
handleCancel={studioData.handleCancel}
templates={templates}
/>
)}
</ScrollToBottom>
<Input
onStop={studioData.stopGenerating}
submittedQuery={studioData.submittedQuery}
isStoppable={studioData.isLoading}
onMessageEditCancel={studioData.onMessageEditCancel}
generationInProgress={
(
studioData.conversation[
studioData.conversation.length - 1
] as ChatMessageServer
)?.isLoading
}
hideMessagesFrom={studioData.hideMessagesFrom}
queryIdToEdit={studioData.queryIdToEdit}
valueToEdit={studioData.inputImperativeValue}
setInputValue={studioData.setInputValue}
value={studioData.inputValue}
setConversation={studioData.setConversation}
conversation={studioData.conversation}
setSubmittedQuery={studioData.setSubmittedQuery}
isInputAtBottom={isScrollable}
projectId={project?.id || '0'}
/>
<DeprecatedClientModal
isOpen={studioData.isDeprecatedModalOpen}
onClose={studioData.closeDeprecatedModal}
/>
</div>
);
};
Expand Down
97 changes: 97 additions & 0 deletions client/src/Project/CurrentTabContent/StudioTab/GeneratedDiff.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Dispatch, memo, SetStateAction, useCallback, useContext } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { GeneratedCodeDiff } from '../../../types/api';
import { BranchIcon, WarningSignIcon } from '../../../icons';
import CodeDiff from '../../../components/Code/CodeDiff';

type Props = {
diff: GeneratedCodeDiff;
onDiffRemoved: (i: number) => void;
onDiffChanged: (i: number, p: string) => void;
applyError?: boolean;
};

const GeneratedDiff = ({
diff,
onDiffRemoved,
onDiffChanged,
applyError,
}: Props) => {
useTranslation();
// const { repositories } = useContext(RepositoriesContext);

// const onDiffClick = useCallback(
// (chunk: DiffChunkType) => {
// const repoFull = repositories?.find((r) => r.ref === chunk.repo);
// if (repoFull) {
// setLeftPanel({
// type: StudioLeftPanelType.DIFF,
// data: {
// filePath: chunk.file,
// repo: repoFull,
// branch: chunk.branch,
// hunks: chunk.hunks,
// },
// });
// }
// },
// [repositories],
// );
const onDiffClick = useCallback(() => {}, []);

return (
<div className="flex flex-col rounded-6 overflow-hidden border border-transparent hover:shadow-medium hover:border-bg-border-hover focus-within:border-bg-main bg-bg-base hover:focus-within:border-bg-main focus-within:shadow-medium transition-all duration-150 ease-in-out">
<div className="w-full bg-bg-shade">
<div
className={`w-full flex items-center justify-center gap-1 py-2 ${
applyError
? 'bg-bg-danger/12 text-bg-danger'
: ' bg-bg-main/15 text-label-link'
} caption`}
>
{applyError ? (
<WarningSignIcon raw sizeClassName="w-3.5 h-3.5" />
) : (
<BranchIcon raw sizeClassName="w-3.5 h-3.5" />
)}
<Trans>
{applyError
? 'Failed to apply the diff'
: 'Generated diffs to be applied'}
</Trans>
</div>
</div>
<div className=" p-4">
<p className="body-s text-label-title">
{diff.chunks.find((c) => c.repo.startsWith('local//')) ? (
<Trans>
The following changes can be applied to your repository. Make sure
the generated diffs are valid before you apply the changes.
</Trans>
) : (
<Trans>
The following changes represent the git diff for the remote
repository. Please note that these changes cannot be applied
directly to a remote repository. Use the &quot;Copy&quot; button
to copy the changes and apply them locally.
</Trans>
)}
</p>
{diff.chunks.map((d, i) => (
<CodeDiff
key={d.file}
filePath={d.file}
language={d.lang || 'diff'}
{...d}
i={i}
onClick={onDiffClick}
onDiffChanged={onDiffChanged}
onDiffRemoved={onDiffRemoved}
/>
))}
</div>
</div>
);
};

export default memo(GeneratedDiff);
Loading

0 comments on commit fce208d

Please sign in to comment.