From bdd39b595e9f5b1f059e35b6de7881949d7bd873 Mon Sep 17 00:00:00 2001 From: anastasiia Date: Fri, 19 Jan 2024 10:28:16 -0500 Subject: [PATCH] add docs to studios --- client/src/CommandBar/Body/Item.tsx | 5 +- client/src/CommandBar/index.tsx | 4 +- .../{AddFileToStudio.tsx => AddToStudio.tsx} | 106 ++-- .../ChatTab/ActionsDropdown.tsx | 12 +- .../CurrentTabContent/ChatTab/index.tsx | 4 +- .../DocTab/ActionsDropdown.tsx | 62 +++ .../CurrentTabContent/DocTab/DocSection.tsx | 80 +++ .../DocTab/RenderedSection.tsx | 61 +++ .../CurrentTabContent/DocTab/index.tsx | 479 ++++++++++++++++++ .../FileTab/ActionsDropdown.tsx | 31 +- .../CurrentTabContent/FileTab/index.tsx | 192 ++++--- .../CurrentTabContent/Header/TabButton.tsx | 29 +- .../StudioTab/ActionsDropdown.tsx | 13 +- .../CurrentTabContent/StudioTab/index.tsx | 4 +- .../src/Project/CurrentTabContent/index.tsx | 10 + .../LeftSidebar/NavPanel/Doc/DocEntry.tsx | 16 +- .../LeftSidebar/NavPanel/Doc/index.tsx | 4 +- .../NavPanel/Studios/StudioEntry.tsx | 20 +- .../NavPanel/Studios/StudioSubItem.tsx | 38 +- .../Project/LeftSidebar/NavPanel/index.tsx | 9 + .../RegexSearchPanel/Results/RepoResult.tsx | 1 + .../MarkdownWithCode/FolderChip.tsx | 1 + client/src/consts/commandBar.ts | 2 - client/src/consts/shortcuts.ts | 4 +- .../context/providers/TabsContextProvider.tsx | 57 ++- client/src/context/tabsContext.tsx | 6 +- client/src/icons/StudioCloseSign.tsx | 20 + client/src/icons/index.ts | 1 + client/src/index.css | 4 +- client/src/locales/en.json | 14 +- client/src/locales/es.json | 14 +- client/src/locales/it.json | 14 +- client/src/locales/ja.json | 14 +- client/src/locales/zh-CN.json | 14 +- client/src/types/api.ts | 2 +- client/src/types/general.ts | 34 +- 36 files changed, 1201 insertions(+), 180 deletions(-) rename client/src/CommandBar/steps/{AddFileToStudio.tsx => AddToStudio.tsx} (64%) create mode 100644 client/src/Project/CurrentTabContent/DocTab/ActionsDropdown.tsx create mode 100644 client/src/Project/CurrentTabContent/DocTab/DocSection.tsx create mode 100644 client/src/Project/CurrentTabContent/DocTab/RenderedSection.tsx create mode 100644 client/src/Project/CurrentTabContent/DocTab/index.tsx create mode 100644 client/src/icons/StudioCloseSign.tsx diff --git a/client/src/CommandBar/Body/Item.tsx b/client/src/CommandBar/Body/Item.tsx index 051ca33ba1..bebfb70bc5 100644 --- a/client/src/CommandBar/Body/Item.tsx +++ b/client/src/CommandBar/Body/Item.tsx @@ -84,10 +84,7 @@ const CommandBarItem = ({ } } else { setChosenStep({ - id: id as Exclude< - CommandBarStepEnum, - CommandBarStepEnum.ADD_FILE_TO_STUDIO - >, + id: id as Exclude, }); } updateArrayInStorage(RECENT_COMMANDS_KEY, itemKey); diff --git a/client/src/CommandBar/index.tsx b/client/src/CommandBar/index.tsx index 3018999df2..35735c1daf 100644 --- a/client/src/CommandBar/index.tsx +++ b/client/src/CommandBar/index.tsx @@ -15,7 +15,7 @@ import ManageRepos from './steps/ManageRepos'; import AddNewRepo from './steps/AddNewRepo'; import ToggleTheme from './steps/ToggleTheme'; import SearchFiles from './steps/SeachFiles'; -import AddFileToStudio from './steps/AddFileToStudio'; +import AddFileToStudio from './steps/AddToStudio'; type Props = {}; @@ -79,7 +79,7 @@ const CommandBar = ({}: Props) => { ) : chosenStep.id === CommandBarStepEnum.SEARCH_FILES ? ( - ) : chosenStep.id === CommandBarStepEnum.ADD_FILE_TO_STUDIO ? ( + ) : chosenStep.id === CommandBarStepEnum.ADD_TO_STUDIO ? ( ) : null} diff --git a/client/src/CommandBar/steps/AddFileToStudio.tsx b/client/src/CommandBar/steps/AddToStudio.tsx similarity index 64% rename from client/src/CommandBar/steps/AddFileToStudio.tsx rename to client/src/CommandBar/steps/AddToStudio.tsx index 72e5c11bbc..546aa72f0d 100644 --- a/client/src/CommandBar/steps/AddFileToStudio.tsx +++ b/client/src/CommandBar/steps/AddToStudio.tsx @@ -9,13 +9,14 @@ import { } from 'react'; import { useTranslation } from 'react-i18next'; import { + AddDocToStudioDataType, AddFileToStudioDataType, CommandBarItemGeneralType, CommandBarSectionType, CommandBarStepEnum, TabTypesEnum, } from '../../types/general'; -import { CodeStudioIcon, PlusSignIcon } from '../../icons'; +import { PlusSignIcon } from '../../icons'; import Header from '../Header'; import Body from '../Body'; import Footer from '../Footer'; @@ -23,10 +24,12 @@ import { CommandBarContext } from '../../context/commandBarContext'; import { ProjectContext } from '../../context/projectContext'; import { TabsContext } from '../../context/tabsContext'; import { postCodeStudio } from '../../services/api'; +import TokenUsage from '../../components/TokenUsage'; +import { TOKEN_LIMIT } from '../../consts/codeStudio'; -type Props = AddFileToStudioDataType & {}; +type Props = (AddFileToStudioDataType | AddDocToStudioDataType) & {}; -const AddFileToStudio = ({ path, repoRef, branch }: Props) => { +const AddToStudio = (props: Props) => { const { t } = useTranslation(); const { setChosenStep } = useContext(CommandBarContext.Handlers); const { project, refreshCurrentProjectStudios } = useContext( @@ -50,15 +53,56 @@ const AddFileToStudio = ({ path, repoRef, branch }: Props) => { if (project?.id) { const newId = await postCodeStudio(project.id); refreshCurrentProjectStudios(); - openNewTab({ - type: TabTypesEnum.FILE, - studioId: newId, - path, - repoRef, - branch, - }); + if ('path' in props) { + openNewTab( + { + type: TabTypesEnum.FILE, + studioId: newId, + ...props, + }, + 'left', + ); + } else { + openNewTab( + { + type: TabTypesEnum.DOC, + studioId: newId, + ...props, + }, + 'left', + ); + } + openNewTab({ type: TabTypesEnum.STUDIO, studioId: newId }, 'right'); } - }, [project?.id, path, repoRef, branch]); + }, [project?.id, props, openNewTab, refreshCurrentProjectStudios]); + + const handleAddToCodeStudio = useCallback( + async (studioId: string) => { + if (project?.id) { + if ('path' in props) { + openNewTab( + { + type: TabTypesEnum.FILE, + studioId, + ...props, + }, + 'left', + ); + } else { + openNewTab( + { + type: TabTypesEnum.DOC, + studioId, + ...props, + }, + 'left', + ); + } + openNewTab({ type: TabTypesEnum.STUDIO, studioId }, 'right'); + } + }, + [project?.id, props, openNewTab], + ); const initialSections = useMemo(() => { return [ @@ -81,22 +125,20 @@ const AddFileToStudio = ({ path, repoRef, branch }: Props) => { { items: (project?.studios || []).map((s) => ({ label: s.name, - Icon: CodeStudioIcon, + Icon: () => ( + + ), + iconContainerClassName: 'bg-transparent', id: s.id, key: s.id, - onClick: () => - openNewTab({ - type: TabTypesEnum.FILE, - studioId: s.id, - path, - repoRef, - branch, - }), + onClick: () => handleAddToCodeStudio(s.id), closeOnClick: true, - // footerHint: t('{{count}} context files used', { - // count: s.token_counts?.per_file.filter((f) => !!f).length, - // }), - footerHint: '', + footerHint: t('{{count}} context files used', { + count: s.context.length, + }), footerBtns: [{ label: t('Add to existing'), shortcut: ['entr'] }], })), label: t('Existing studio conversations'), @@ -104,15 +146,7 @@ const AddFileToStudio = ({ path, repoRef, branch }: Props) => { key: 'studio-items', }, ]; - }, [ - t, - project?.studios, - handleNewCodeStudio, - openNewTab, - path, - repoRef, - branch, - ]); + }, [t, project?.studios, handleNewCodeStudio, openNewTab, props]); useEffect(() => { if (!inputValue) { @@ -140,8 +174,8 @@ const AddFileToStudio = ({ path, repoRef, branch }: Props) => { }, [initialSections, inputValue]); const breadcrumbs = useMemo(() => { - return [t('Add file to studio')]; - }, []); + return [t(`Add ${'path' in props ? 'file' : 'doc'} to studio`)]; + }, [props, t]); return (
@@ -158,4 +192,4 @@ const AddFileToStudio = ({ path, repoRef, branch }: Props) => { ); }; -export default memo(AddFileToStudio); +export default memo(AddToStudio); diff --git a/client/src/Project/CurrentTabContent/ChatTab/ActionsDropdown.tsx b/client/src/Project/CurrentTabContent/ChatTab/ActionsDropdown.tsx index 4a99cce624..d3450941df 100644 --- a/client/src/Project/CurrentTabContent/ChatTab/ActionsDropdown.tsx +++ b/client/src/Project/CurrentTabContent/ChatTab/ActionsDropdown.tsx @@ -1,9 +1,10 @@ -import { memo, useCallback, useMemo } from 'react'; +import { memo, useCallback } 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 { openInSplitViewShortcut } from '../../../consts/shortcuts'; type Props = { handleMoveToAnotherSide: () => void; @@ -41,20 +42,13 @@ const ActionsDropdown = ({ side, ]); - const shortcuts = useMemo(() => { - return { - splitView: ['cmd', ']'], - }; - }, []); - return (
} /> {conversationId && ( diff --git a/client/src/Project/CurrentTabContent/ChatTab/index.tsx b/client/src/Project/CurrentTabContent/ChatTab/index.tsx index d223c000d1..d714c5d4c6 100644 --- a/client/src/Project/CurrentTabContent/ChatTab/index.tsx +++ b/client/src/Project/CurrentTabContent/ChatTab/index.tsx @@ -19,8 +19,8 @@ import { TabsContext } from '../../../context/tabsContext'; import { ChatTabType } from '../../../types/general'; import { ProjectContext } from '../../../context/projectContext'; import { CommandBarContext } from '../../../context/commandBarContext'; -import { openInSplitViewShortcut } from '../../../consts/commandBar'; import { UIContext } from '../../../context/uiContext'; +import { openInSplitViewShortcut } from '../../../consts/shortcuts'; import Conversation from './Conversation'; import ActionsDropdown from './ActionsDropdown'; @@ -70,7 +70,7 @@ const ChatTab = ({ const handleKeyEvent = useCallback( (e: KeyboardEvent) => { - if (checkEventKeys(e, ['cmd', ']'])) { + if (checkEventKeys(e, openInSplitViewShortcut)) { handleMoveToAnotherSide(); } }, diff --git a/client/src/Project/CurrentTabContent/DocTab/ActionsDropdown.tsx b/client/src/Project/CurrentTabContent/DocTab/ActionsDropdown.tsx new file mode 100644 index 0000000000..c7a551ebde --- /dev/null +++ b/client/src/Project/CurrentTabContent/DocTab/ActionsDropdown.tsx @@ -0,0 +1,62 @@ +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import DropdownSection from '../../../components/Dropdown/Section'; +import SectionItem from '../../../components/Dropdown/Section/SectionItem'; +import { + SplitViewIcon, + StudioCloseSignIcon, + StudioPlusSignIcon, +} from '../../../icons'; +import { + addToStudioShortcut, + openInSplitViewShortcut, + removeFromStudioShortcut, +} from '../../../consts/shortcuts'; + +type Props = { + handleMoveToAnotherSide: () => void; + handleAddToStudio: () => void; + handleRemoveFromStudio: () => void; + isDocInContext: boolean; +}; + +const ActionsDropdown = ({ + handleMoveToAnotherSide, + handleAddToStudio, + handleRemoveFromStudio, + isDocInContext, +}: Props) => { + const { t } = useTranslation(); + + return ( +
+ + {isDocInContext ? ( + } + /> + ) : ( + } + /> + )} + + + } + /> + +
+ ); +}; + +export default memo(ActionsDropdown); diff --git a/client/src/Project/CurrentTabContent/DocTab/DocSection.tsx b/client/src/Project/CurrentTabContent/DocTab/DocSection.tsx new file mode 100644 index 0000000000..2365f76052 --- /dev/null +++ b/client/src/Project/CurrentTabContent/DocTab/DocSection.tsx @@ -0,0 +1,80 @@ +import React, { Dispatch, memo, SetStateAction, useCallback } from 'react'; +import { Trans } from 'react-i18next'; +import Button from '../../../components/Button'; +import { DocSectionType } from '../../../types/api'; +import RenderedSection from './RenderedSection'; + +type Props = DocSectionType & { + isSelected: boolean; + isNothingSelected: boolean; + isEditingSelection: boolean; + setSelectedSections: Dispatch>; +}; + +const DocSection = ({ + text, + isSelected, + setSelectedSections, + point_id, + isNothingSelected, + doc_source, + isEditingSelection, +}: Props) => { + const setSelected = useCallback( + (b: boolean) => { + setSelectedSections((prev) => { + if (b) { + return [...prev, point_id]; + } + return prev.filter((r) => r !== point_id); + }); + }, + [point_id, setSelectedSections], + ); + + const handleClick = useCallback(() => { + if (isEditingSelection) { + setSelected(!isSelected); + } + }, [isSelected, isEditingSelection]); + return ( +
+ {isEditingSelection && ( +
+ +
+ )} + +
+ ); +}; + +export default memo(DocSection); diff --git a/client/src/Project/CurrentTabContent/DocTab/RenderedSection.tsx b/client/src/Project/CurrentTabContent/DocTab/RenderedSection.tsx new file mode 100644 index 0000000000..6c40c09faa --- /dev/null +++ b/client/src/Project/CurrentTabContent/DocTab/RenderedSection.tsx @@ -0,0 +1,61 @@ +import React, { memo, useCallback, useContext, useMemo } from 'react'; +import { Remarkable } from 'remarkable'; +import { highlightCode } from '../../../utils/prism'; +import { DeviceContext } from '../../../context/deviceContext'; + +const md = new Remarkable({ + html: false, + highlight(str: string, lang: string): string { + try { + return highlightCode(str, lang); + } catch (err) { + console.log(err); + return ''; + } + }, + linkTarget: '__blank', +}); + +type Props = { + text: string; + baseUrl: string; + isEditingSelection: boolean; +}; + +const RenderedSection = ({ text, baseUrl, isEditingSelection }: Props) => { + const { openLink } = useContext(DeviceContext); + + const markdown = useMemo(() => md.render(text), [text]); + + const handleClick = useCallback( + (e: React.MouseEvent) => { + // @ts-ignore + const href = e.target.getAttribute('href'); + if (href) { + e.preventDefault(); + e.stopPropagation(); + openLink( + href.startsWith('http://') || href.startsWith('https://') + ? href + : new URL(href, baseUrl).href, + ); + } + }, + [openLink, baseUrl], + ); + + return ( +
+
+
+ ); +}; + +export default memo(RenderedSection); diff --git a/client/src/Project/CurrentTabContent/DocTab/index.tsx b/client/src/Project/CurrentTabContent/DocTab/index.tsx new file mode 100644 index 0000000000..8f4c628fd4 --- /dev/null +++ b/client/src/Project/CurrentTabContent/DocTab/index.tsx @@ -0,0 +1,479 @@ +import React, { + memo, + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { CommandBarStepEnum, DocTabType } from '../../../types/general'; +import { + MagazineIcon, + MoreHorizontalIcon, + SplitViewIcon, + StudioCloseSignIcon, + StudioPlusSignIcon, +} from '../../../icons'; +import Dropdown from '../../../components/Dropdown'; +import Button from '../../../components/Button'; +import { TabsContext } from '../../../context/tabsContext'; +import { checkEventKeys } from '../../../utils/keyboardUtils'; +import useKeyboardNavigation from '../../../hooks/useKeyboardNavigation'; +import { UIContext } from '../../../context/uiContext'; +import { CommandBarContext } from '../../../context/commandBarContext'; +import { + addToStudioShortcut, + openInSplitViewShortcut, + removeFromStudioShortcut, +} from '../../../consts/shortcuts'; +import { + getCodeStudio, + getDocSections, + getDocTokenCount, + getIndexedPages, + patchCodeStudio, +} from '../../../services/api'; +import { + CodeStudioType, + DocPageType, + DocSectionType, +} from '../../../types/api'; +import { findElementInCurrentTab } from '../../../utils/domUtils'; +import { ProjectContext } from '../../../context/projectContext'; +import Badge from '../../../components/Badge'; +import { humanNumber } from '../../../utils'; +import ActionsDropdown from './ActionsDropdown'; +import DocSection from './DocSection'; + +type Props = DocTabType & { + noBorder?: boolean; + side: 'left' | 'right'; + tabKey: string; + handleMoveToAnotherSide: () => void; +}; + +const DocTab = ({ + side, + tabKey, + handleMoveToAnotherSide, + docId, + title, + favicon, + noBorder, + relativeUrl, + studioId, + initialSections, + isDocInContext, +}: Props) => { + const { t } = useTranslation(); + const { focusedPanel } = useContext(TabsContext.All); + const { updateTabProperty } = useContext(TabsContext.Handlers); + const { isLeftSidebarFocused } = useContext(UIContext.Focus); + const { setFocusedTabItems, setChosenStep, setIsVisible } = useContext( + CommandBarContext.Handlers, + ); + const { isVisible: isCommandBarVisible } = useContext( + CommandBarContext.General, + ); + const { project, refreshCurrentProjectStudios } = useContext( + ProjectContext.Current, + ); + const [fullDoc, setFullDoc] = useState(null); + const [studio, setStudio] = useState(null); + const [sections, setSections] = useState([]); + const [selectedSections, setSelectedSections] = useState( + initialSections || [], + ); + const [tokenCount, setTokenCount] = useState(0); + const [isEditingSelection, setIsEditingSelection] = useState(false); + + const refreshStudio = useCallback(() => { + if (studioId && project?.id) { + getCodeStudio(project.id, studioId).then(setStudio); + } else { + setStudio(null); + } + }, [studioId, project?.id]); + + useEffect(() => { + refreshStudio(); + }, [refreshStudio]); + + useEffect(() => { + getIndexedPages(docId).then((resp) => { + const doc = resp.find((p) => p.relative_url === relativeUrl); + if (doc) { + setFullDoc(doc); + } + }); + getDocSections(docId, relativeUrl).then((resp) => { + setSections(resp); + }); + }, [docId, relativeUrl]); + + useEffect(() => { + if (project?.id) { + getDocTokenCount(project.id, docId, relativeUrl, selectedSections).then( + setTokenCount, + ); + } + }, [selectedSections, relativeUrl, docId, project?.id]); + + useEffect(() => { + if (initialSections?.length && sections.length) { + const firstSelectedSection = + initialSections?.length === 1 + ? initialSections[0] + : initialSections?.length + ? sections.find((s) => initialSections?.includes(s.point_id)) + ?.point_id + : ''; + findElementInCurrentTab( + `[data-active="true"][data-section-id="${firstSelectedSection}"]`, + )?.scrollIntoView(); + } + }, [sections.length, initialSections]); + + const handleAddToStudio = useCallback(() => { + setChosenStep({ + id: CommandBarStepEnum.ADD_TO_STUDIO, + data: { docId, relativeUrl, favicon, title }, + }); + setIsVisible(true); + }, [docId, relativeUrl, favicon, title]); + + const handleRemoveFromStudio = useCallback(async () => { + if (project?.id && studioId && studio) { + const patchedDoc = studio?.doc_context.find( + (d) => d.doc_id === docId && d.relative_url === relativeUrl, + ); + if (patchedDoc) { + await patchCodeStudio(project.id, studioId, { + doc_context: studio?.doc_context.filter( + (d) => d.doc_id !== docId || d.relative_url !== relativeUrl, + ), + }); + refreshCurrentProjectStudios(); + refreshStudio(); + setIsEditingSelection(false); + updateTabProperty( + tabKey, + 'isDocInContext', + false, + side, + ); + updateTabProperty( + tabKey, + 'initialSections', + undefined, + side, + ); + updateTabProperty( + tabKey, + 'studioId', + undefined, + side, + ); + setStudio(null); + setSelectedSections([]); + } + } + }, [docId, relativeUrl, project?.id, studioId, studio]); + + const dropdownComponentProps = useMemo(() => { + return { + handleMoveToAnotherSide, + handleAddToStudio, + handleRemoveFromStudio, + isDocInContext, + }; + }, [ + handleMoveToAnotherSide, + handleAddToStudio, + handleRemoveFromStudio, + isDocInContext, + ]); + + useEffect(() => { + if (focusedPanel === side) { + setFocusedTabItems([ + { + label: t('Open in split view'), + Icon: SplitViewIcon, + id: 'split_view', + key: 'split_view', + onClick: handleMoveToAnotherSide, + closeOnClick: true, + shortcut: openInSplitViewShortcut, + footerHint: '', + footerBtns: [{ label: t('Move'), shortcut: ['entr'] }], + }, + ...(studioId + ? [ + { + label: t('Remove from studio'), + Icon: StudioCloseSignIcon, + id: 'doc_from_studio', + key: 'doc_from_studio', + onClick: handleRemoveFromStudio, + shortcut: removeFromStudioShortcut, + footerHint: t('Remove page from code studio context'), + footerBtns: [{ label: t('Remove'), shortcut: ['entr'] }], + }, + ] + : [ + { + label: t('Add to studio'), + Icon: StudioPlusSignIcon, + id: 'doc_to_studio', + key: 'doc_to_studio', + onClick: handleAddToStudio, + shortcut: addToStudioShortcut, + footerHint: t('Add file to code studio context'), + footerBtns: [{ label: t('Add'), shortcut: ['entr'] }], + }, + ]), + ]); + } + }, [ + focusedPanel, + side, + handleMoveToAnotherSide, + handleRemoveFromStudio, + handleAddToStudio, + ]); + + const hasChanges = useMemo(() => { + return ( + (studioId && !isDocInContext) || + JSON.stringify(initialSections) !== JSON.stringify(selectedSections) + ); + }, [studioId, isDocInContext, initialSections, selectedSections]); + + const handleEditRanges = useCallback(() => { + setIsEditingSelection(true); + }, []); + + useEffect(() => { + if (studioId && !isDocInContext) { + handleEditRanges(); + } + }, [studioId, isDocInContext, handleEditRanges]); + + const handleCancelStudio = useCallback(() => { + setIsEditingSelection(false); + if (isDocInContext) { + setSelectedSections(initialSections || []); + } else { + setSelectedSections([]); + updateTabProperty( + tabKey, + 'studioId', + undefined, + side, + ); + } + }, [tabKey, side, isDocInContext, initialSections]); + + const handleSubmitToStudio = useCallback(async () => { + if (project?.id && studioId && studio) { + const patchedDoc = studio?.doc_context.find( + (f) => + f.doc_id === docId && + f.doc_source === fullDoc?.doc_source && + f.relative_url === relativeUrl, + ); + if (!patchedDoc) { + await patchCodeStudio(project.id, studioId, { + doc_context: [ + ...(studio?.doc_context || []), + { + doc_id: docId, + doc_source: fullDoc?.doc_source || '', + doc_icon: favicon || '', + doc_title: title || '', + relative_url: relativeUrl, + absolute_url: fullDoc?.absolute_url || '', + ranges: selectedSections, + hidden: false, + }, + ], + }); + } else { + patchedDoc.ranges = selectedSections; + const newContext = studio?.doc_context + .filter( + (f) => + f.doc_id !== docId || + f.doc_source !== fullDoc?.doc_source || + f.relative_url !== relativeUrl, + ) + .concat(patchedDoc); + await patchCodeStudio(project.id, studioId, { + doc_context: newContext, + }); + } + refreshCurrentProjectStudios(); + refreshStudio(); + setIsEditingSelection(false); + updateTabProperty( + tabKey, + 'isDocInContext', + true, + side, + ); + updateTabProperty( + tabKey, + 'initialSections', + selectedSections, + side, + ); + } + }, [ + project?.id, + studio, + docId, + relativeUrl, + fullDoc, + studioId, + selectedSections, + ]); + + const handleKeyEvent = useCallback( + (e: KeyboardEvent) => { + if (checkEventKeys(e, openInSplitViewShortcut)) { + handleMoveToAnotherSide(); + } else if (checkEventKeys(e, addToStudioShortcut)) { + e.preventDefault(); + e.stopPropagation(); + handleAddToStudio(); + } else if (checkEventKeys(e, removeFromStudioShortcut)) { + e.preventDefault(); + e.stopPropagation(); + handleRemoveFromStudio(); + } + }, + [handleMoveToAnotherSide, handleAddToStudio], + ); + useKeyboardNavigation( + handleKeyEvent, + focusedPanel !== side || isLeftSidebarFocused || isCommandBarVisible, + ); + + return ( +
+
+
+ {favicon ? ( + {relativeUrl} + ) : ( + + )} + {title || relativeUrl} + {!!studio && studioId && ( + <> +
+ +

1500 + ? 'text-yellow' + : tokenCount < 500 + ? 'text-green' + : 'text-red' + } code-mini`} + > + {humanNumber(tokenCount)}{' '} + # tokens +

+ + )} +
+ {focusedPanel === side && + (studioId && (hasChanges || isEditingSelection) ? ( +
+ {!isEditingSelection && ( + <> + +
+ + )} + + +
+ ) : ( + studioId && ( +
+ +
+ ) + ))} + {!isEditingSelection && ( + + + + )} +
+
+ {sections.map((s) => { + return ( + + ); + })} +
+
+ ); +}; + +export default memo(DocTab); diff --git a/client/src/Project/CurrentTabContent/FileTab/ActionsDropdown.tsx b/client/src/Project/CurrentTabContent/FileTab/ActionsDropdown.tsx index 921eb3c276..80ed3db76c 100644 --- a/client/src/Project/CurrentTabContent/FileTab/ActionsDropdown.tsx +++ b/client/src/Project/CurrentTabContent/FileTab/ActionsDropdown.tsx @@ -6,23 +6,29 @@ import { SplitViewIcon, FileWithSparksIcon, StudioPlusSignIcon, + StudioCloseSignIcon, } from '../../../icons'; -import { openInSplitViewShortcut } from '../../../consts/commandBar'; import { - addFileToStudioShortcut, + addToStudioShortcut, + openInSplitViewShortcut, explainFileShortcut, + removeFromStudioShortcut, } from '../../../consts/shortcuts'; type Props = { handleExplain: () => void; handleMoveToAnotherSide: () => void; handleAddToStudio: () => void; + handleRemoveFromStudio: () => void; + isFileInContext: boolean; }; const ActionsDropdown = ({ handleExplain, handleMoveToAnotherSide, handleAddToStudio, + handleRemoveFromStudio, + isFileInContext, }: Props) => { const { t } = useTranslation(); @@ -35,12 +41,21 @@ const ActionsDropdown = ({ shortcut={explainFileShortcut} icon={} /> - } - /> + {isFileInContext ? ( + } + /> + ) : ( + } + /> + )} { if (studioId && project?.id) { getCodeStudio(project.id, studioId).then(setStudio); - } else if (project?.id) { + } else { setStudio(null); } }, [studioId, project?.id]); @@ -278,12 +280,50 @@ const FileTab = ({ const handleAddToStudio = useCallback(() => { setChosenStep({ - id: CommandBarStepEnum.ADD_FILE_TO_STUDIO, + id: CommandBarStepEnum.ADD_TO_STUDIO, data: { path, repoRef, branch }, }); setIsVisible(true); }, [path, repoRef, branch]); + const handleRemoveFromStudio = useCallback(async () => { + if (project?.id && studioId && studio) { + const patchedFile = studio?.context.find( + (f) => f.path === path && f.repo === repoRef && f.branch === branch, + ); + if (patchedFile) { + await patchCodeStudio(project.id, studioId, { + context: studio?.context.filter( + (f) => f.path !== path || f.repo !== repoRef || f.branch !== branch, + ), + }); + refreshCurrentProjectStudios(); + refreshStudio(); + setIsEditingRanges(false); + updateTabProperty( + tabKey, + 'isFileInContext', + false, + side, + ); + updateTabProperty( + tabKey, + 'initialRanges', + undefined, + side, + ); + updateTabProperty( + tabKey, + 'studioId', + undefined, + side, + ); + setStudio(null); + setSelectedLines([]); + } + } + }, [path, repoRef, branch, project?.id, studioId, studio]); + const handleSubmitToStudio = useCallback(async () => { if (project?.id && studioId && studio) { const patchedFile = studio?.context.find( @@ -337,10 +377,10 @@ const FileTab = ({ const hasChanges = useMemo(() => { return ( - !isFileInContext || + (studioId && !isFileInContext) || JSON.stringify(initialRanges) !== JSON.stringify(selectedLines) ); - }, [isFileInContext, initialRanges, selectedLines]); + }, [studioId, isFileInContext, initialRanges, selectedLines]); const handleKeyEvent = useCallback( (e: KeyboardEvent) => { @@ -348,10 +388,14 @@ const FileTab = ({ handleExplain(); } else if (checkEventKeys(e, openInSplitViewShortcut)) { handleMoveToAnotherSide(); - } else if (checkEventKeys(e, addFileToStudioShortcut)) { + } else if (checkEventKeys(e, addToStudioShortcut)) { e.preventDefault(); e.stopPropagation(); handleAddToStudio(); + } else if (checkEventKeys(e, removeFromStudioShortcut)) { + e.preventDefault(); + e.stopPropagation(); + handleRemoveFromStudio(); } }, [handleExplain, handleMoveToAnotherSide, handleAddToStudio], @@ -379,7 +423,18 @@ const FileTab = ({ footerBtns: [{ label: t('Explain'), shortcut: ['entr'] }], }, ...(studioId - ? [] + ? [ + { + label: t('Remove from studio'), + Icon: StudioCloseSignIcon, + id: 'file_from_studio', + key: 'file_from_studio', + onClick: handleRemoveFromStudio, + shortcut: removeFromStudioShortcut, + footerHint: t('Remove file from code studio context'), + footerBtns: [{ label: t('Remove'), shortcut: ['entr'] }], + }, + ] : [ { label: t('Add to studio'), @@ -387,7 +442,7 @@ const FileTab = ({ id: 'file_to_studio', key: 'file_to_studio', onClick: handleAddToStudio, - shortcut: addFileToStudioShortcut, + shortcut: addToStudioShortcut, footerHint: t('Add file to code studio context'), footerBtns: [{ label: t('Add'), shortcut: ['entr'] }], }, @@ -419,8 +474,16 @@ const FileTab = ({ handleExplain, handleMoveToAnotherSide, handleAddToStudio, + handleRemoveFromStudio, + isFileInContext, }; - }, [handleExplain, handleMoveToAnotherSide, handleAddToStudio]); + }, [ + handleExplain, + handleMoveToAnotherSide, + handleAddToStudio, + handleRemoveFromStudio, + isFileInContext, + ]); return (
-
+
- {!!studio && ( + {!!studio && studioId && ( <>
-

+

1500 + ? 'text-yellow' + : tokenCount < 500 + ? 'text-green' + : 'text-red' + } code-mini`} + > {humanNumber(tokenCount)}{' '} # tokens

@@ -449,37 +520,37 @@ const FileTab = ({ )}
{focusedPanel === side && - (studio ? ( - hasChanges || isEditingRanges ? ( -
- {!isEditingRanges && ( - <> - -
- - )} - - -
- ) : ( + (studioId && (hasChanges || isEditingRanges) ? ( +
+ {!isEditingRanges && ( + <> + +
+ + )} + + +
+ ) : ( + studioId && (
) - ) : ( - - - ))} + {!isEditingRanges && ( + + + + )}
{ const { t } = useTranslation(); const { closeTab, setActiveLeftTab, setActiveRightTab, setFocusedPanel } = @@ -160,6 +171,9 @@ const TabButton = ({ isFileInContext, conversationId, initialQuery, + favicon, + relativeUrl, + docId, }, side, }; @@ -195,6 +209,9 @@ const TabButton = ({ isFileInContext, conversationId, initialQuery, + docId, + relativeUrl, + favicon, }); setFocusedPanel(side); }, [ @@ -238,8 +255,12 @@ const TabButton = ({ sizeClassName="w-4 h-4" className="text-brand-default" /> - ) : ( + ) : type === TabTypesEnum.STUDIO ? ( + ) : favicon ? ( + {title} + ) : ( + )}

diff --git a/client/src/components/MarkdownWithCode/FolderChip.tsx b/client/src/components/MarkdownWithCode/FolderChip.tsx index d6bdb49989..59d3e16eef 100644 --- a/client/src/components/MarkdownWithCode/FolderChip.tsx +++ b/client/src/components/MarkdownWithCode/FolderChip.tsx @@ -66,6 +66,7 @@ const FolderChip = ({ onClick, path, repoRef }: Props) => { focusedIndex={''} index={'0'} isLeftSidebarFocused={false} + isCommandBarVisible />
diff --git a/client/src/consts/commandBar.ts b/client/src/consts/commandBar.ts index 18e323b963..fac009bb1c 100644 --- a/client/src/consts/commandBar.ts +++ b/client/src/consts/commandBar.ts @@ -3,5 +3,3 @@ export const PRIVATE_REPOS = 'private_repos'; export const PUBLIC_REPOS = 'public_repos'; export const LOCAL_REPOS = 'local_repos'; export const DOCUMENTATION = 'documentation'; - -export const openInSplitViewShortcut = ['cmd', ']']; diff --git a/client/src/consts/shortcuts.ts b/client/src/consts/shortcuts.ts index b0a44a2f48..7fff961e1a 100644 --- a/client/src/consts/shortcuts.ts +++ b/client/src/consts/shortcuts.ts @@ -2,5 +2,7 @@ export const regexToggleShortcut = ['cmd', '/']; export const newChatTabShortcut = ['option', 'N']; export const newStudioTabShortcut = ['option', 'shift', 'N']; export const explainFileShortcut = ['cmd', 'E']; -export const addFileToStudioShortcut = ['cmd', 'shift', '+']; +export const addToStudioShortcut = ['cmd', 'shift', '+']; +export const removeFromStudioShortcut = ['cmd', 'shift', '-']; export const useTemplateShortcut = ['cmd', 'T']; +export const openInSplitViewShortcut = ['cmd', ']']; diff --git a/client/src/context/providers/TabsContextProvider.tsx b/client/src/context/providers/TabsContextProvider.tsx index ed313f84a7..97070a07df 100644 --- a/client/src/context/providers/TabsContextProvider.tsx +++ b/client/src/context/providers/TabsContextProvider.tsx @@ -10,9 +10,10 @@ import { import { useSearchParams } from 'react-router-dom'; import { TabsContext } from '../tabsContext'; import { - StudioTabType, ChatTabType, + DocTabType, FileTabType, + StudioTabType, TabType, TabTypesEnum, } from '../../types/general'; @@ -48,6 +49,7 @@ const TabsContextProvider = ({ children }: PropsWithChildren) => { data: | Omit | Omit + | Omit | Omit, forceSide?: 'left' | 'right', ) => { @@ -103,10 +105,16 @@ const TabsContextProvider = ({ children }: PropsWithChildren) => { conversationId: data.conversationId, title: data.title, } - : { + : data.type === TabTypesEnum.STUDIO + ? { type: TabTypesEnum.STUDIO, key: data.studioId, studioId: data.studioId, + } + : { + ...data, + type: TabTypesEnum.DOC, + key: data.docId + data.relativeUrl, }; const previousTab = prev.find((t) => newTab.type === TabTypesEnum.CHAT && newTab.conversationId @@ -144,6 +152,22 @@ const TabsContextProvider = ({ children }: PropsWithChildren) => { newTabs[previousTabIndex] = t; setActiveTabAction(t); return newTabs; + } else if ( + previousTab.type === TabTypesEnum.DOC && + newTab.type === TabTypesEnum.DOC && + previousTab.studioId !== newTab.studioId + ) { + const previousTabIndex = prev.findIndex( + (t) => t.key === newTab.key, + ); + const newTabs = [...prev]; + const t = { + ...previousTab, + studioId: newTab.studioId, + }; + newTabs[previousTabIndex] = t; + setActiveTabAction(t); + return newTabs; } else { setActiveTabAction(previousTab); } @@ -204,6 +228,19 @@ const TabsContextProvider = ({ children }: PropsWithChildren) => { if (activeTab.title) { newParams.title = activeTab.title; } + } else if (activeTab.type === TabTypesEnum.DOC) { + if (activeTab.docId) { + newParams.docId = activeTab.docId; + } + if (activeTab.title) { + newParams.title = activeTab.title; + } + if (activeTab.favicon) { + newParams.favicon = activeTab.favicon; + } + if (activeTab.relativeUrl) { + newParams.relativeUrl = activeTab.relativeUrl; + } } setSearchParams(newParams, { replace: true }); } @@ -221,7 +258,8 @@ const TabsContextProvider = ({ children }: PropsWithChildren) => { const path = searchParams.get('path'); const conversationId = searchParams.get('conversationId'); const studioId = searchParams.get('studioId'); - if (!activeLeftTab && (path || conversationId || studioId)) { + const docId = searchParams.get('docId'); + if (!activeLeftTab && (path || conversationId || studioId || docId)) { const repoRef = searchParams.get('repoRef'); if (path && repoRef) { openNewTab({ @@ -238,6 +276,14 @@ const TabsContextProvider = ({ children }: PropsWithChildren) => { conversationId, title: searchParams.get('title') || undefined, }); + } else if (docId) { + openNewTab({ + type: TabTypesEnum.DOC, + docId, + title: searchParams.get('title') || undefined, + favicon: searchParams.get('favicon') || undefined, + relativeUrl: searchParams.get('relativeUrl')!, + }); } else if (studioId) { openNewTab({ type: TabTypesEnum.STUDIO, @@ -275,7 +321,10 @@ const TabsContextProvider = ({ children }: PropsWithChildren) => { }, []); const updateTabProperty = useCallback( - ( + < + T extends ChatTabType | FileTabType | StudioTabType | DocTabType, + K extends keyof T, + >( tabKey: string, objectKey: K, newValue: T[K], diff --git a/client/src/context/tabsContext.tsx b/client/src/context/tabsContext.tsx index 75b66c15fb..3e88ebec1e 100644 --- a/client/src/context/tabsContext.tsx +++ b/client/src/context/tabsContext.tsx @@ -1,6 +1,7 @@ import { createContext, Dispatch, SetStateAction } from 'react'; import { ChatTabType, + DocTabType, FileTabType, StudioTabType, TabType, @@ -11,7 +12,8 @@ type HandlersContextType = { data: | Omit | Omit - | Omit, + | Omit + | Omit, forceSide?: 'left' | 'right', ) => void; closeTab: (key: string, side: 'left' | 'right') => void; @@ -21,7 +23,7 @@ type HandlersContextType = { setLeftTabs: Dispatch>; setRightTabs: Dispatch>; updateTabProperty: < - T extends ChatTabType | FileTabType | StudioTabType, + T extends ChatTabType | FileTabType | StudioTabType | DocTabType, K extends keyof T, >( tabKey: string, diff --git a/client/src/icons/StudioCloseSign.tsx b/client/src/icons/StudioCloseSign.tsx new file mode 100644 index 0000000000..cdfadfcfa9 --- /dev/null +++ b/client/src/icons/StudioCloseSign.tsx @@ -0,0 +1,20 @@ +import IconWrapper from './Wrapper'; + +const RawIcon = ( + + + + +); + +export default IconWrapper(RawIcon); diff --git a/client/src/icons/index.ts b/client/src/icons/index.ts index 2d56f2eaa4..d3b165d765 100644 --- a/client/src/icons/index.ts +++ b/client/src/icons/index.ts @@ -55,6 +55,7 @@ export { default as RepositoryIcon } from './Repository'; export { default as RunIcon } from './Run'; export { default as ShapesIcon } from './Shapes'; export { default as SplitViewIcon } from './SplitView'; +export { default as StudioCloseSignIcon } from './StudioCloseSign'; export { default as StudioPlusSignIcon } from './StudioPlusSign'; export { default as TemplateIcon } from './Template'; export { default as TemplatesIcon } from './Templates'; diff --git a/client/src/index.css b/client/src/index.css index 382f4368f9..a55ccb9888 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -1002,9 +1002,7 @@ h5, .h5 { scrollbar-width: initial; /* Firefox */ } -.readme h1, .readme h2, .readme h3, .readme h4, .readme h5, .readme h6, .readme p, .readme span, .readme div, -.doc-section h1, .doc-section h2, .doc-section h3, .doc-section h4, .doc-section h5, .doc-section h6, -.doc-section p, .doc-section span, .doc-section div { +.readme h1, .readme h2, .readme h3, .readme h4, .readme h5, .readme h6, .readme p, .readme span, .readme div { cursor: auto; } diff --git a/client/src/locales/en.json b/client/src/locales/en.json index c0e95b3153..f083806a0c 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -614,6 +614,8 @@ "Add to studio": "Add to studio", "Add file to code studio context": "Add file to code studio context", "{{count}} context files used": "{{count}} context files used", + "{{count}} context files used_one": "{{count}} context file used", + "{{count}} context files used_other": "{{count}} context files used", "Existing studio conversations": "Existing studio conversations", "Add file to studio": "Add file to studio", "Add to existing": "Add to existing", @@ -641,5 +643,15 @@ "Search templates...": "Search templates...", "Manage templates": "Manage templates", "Prompts": "Prompts", - "Manage docs": "Manage docs" + "Manage docs": "Manage docs", + "Add doc to studio": "Add doc to studio", + "Whole page": "Whole page", + "Select sections": "Select sections", + "Edit sections": "Edit sections", + "Documentation in studio": "Documentation in studio", + "# selected section_one": "{{count}} selected section", + "# selected section_other": "{{count}} selected sections", + "Remove from studio": "Remove from studio", + "Remove file from code studio context": "Remove file from code studio context", + "Remove page from code studio context": "Remove page from code studio context" } \ No newline at end of file diff --git a/client/src/locales/es.json b/client/src/locales/es.json index 7f8d64a63e..9e6954e000 100644 --- a/client/src/locales/es.json +++ b/client/src/locales/es.json @@ -611,6 +611,8 @@ "Add to existing": "Agregar a la existencia", "Existing studio conversations": "Conversaciones de estudio existentes", "{{count}} context files used": "{{count}} archivos de contexto utilizados", + "{{count}} context files used_one": "{{count}} archivo de contexto utilizado", + "{{count}} context files used_other": "{{count}} archivos de contexto utilizados", "Add file to studio": "Agregar archivo al estudio", "Search studio conversations...": "Buscar conversaciones de estudio...", "Close all tabs": "Cierra todas las pestañas", @@ -641,5 +643,15 @@ "Manage templates": "Administrar plantillas", "Prompts": "Indicaciones", "Manage docs": "Administrar documentos", - "Paste a link to any documentation web page": "Pegue un enlace a cualquier página web de documentación" + "Paste a link to any documentation web page": "Pegue un enlace a cualquier página web de documentación", + "Add doc to studio": "Agregar documento al estudio", + "Whole page": "Toda la pagina", + "Select sections": "Seleccionar secciones", + "Edit sections": "Editar secciones", + "Documentation in studio": "Documentación en estudio", + "# selected section_one": "{{count}} sección seleccionada", + "# selected section_other": "{{count}} secciónes seleccionadas", + "Remove from studio": "Retirar del estudio", + "Remove file from code studio context": "Eliminar el archivo del contexto de código de código", + "Remove page from code studio context": "Eliminar la página del contexto del estudio de código" } \ No newline at end of file diff --git a/client/src/locales/it.json b/client/src/locales/it.json index 84068b70c5..98c66f3257 100644 --- a/client/src/locales/it.json +++ b/client/src/locales/it.json @@ -578,6 +578,8 @@ "Add to existing": "Aggiungi a esistente", "Search studio conversations...": "Cerca conversazioni in studio ...", "{{count}} context files used": "{{count}} file di contesto utilizzati", + "{{count}} context files used_one": "{{count}} file di contesto utilizzato", + "{{count}} context files used_other": "{{count}} file di contesto utilizzati", "Add file to studio": "Aggiungi file a Studio", "Close all tabs": "Chiudi tutte le schede", "Create ranges": "Crea gamme", @@ -603,5 +605,15 @@ "Search templates...": "Modelli di ricerca ...", "Manage templates": "Gestisci modelli", "Prompts": "Richiede", - "Manage docs": "Gestisci documenti" + "Manage docs": "Gestisci documenti", + "Add doc to studio": "Aggiungi doc a studio", + "Whole page": "Pagina intera", + "Select sections": "Seleziona le sezioni", + "Edit sections": "Modifica sezioni", + "Documentation in studio": "Documentazione in studio", + "# selected section_one": "{{count}} sezione selezionata", + "# selected section_other": "{{count}} sezioni selezionate", + "Remove from studio": "Togliere dallo studio", + "Remove file from code studio context": "Rimuovi il file dal contesto Code Studio", + "Remove page from code studio context": "Rimuovi la pagina dal contesto di Code Studio" } \ No newline at end of file diff --git a/client/src/locales/ja.json b/client/src/locales/ja.json index 72ba295442..050e1f31a8 100644 --- a/client/src/locales/ja.json +++ b/client/src/locales/ja.json @@ -599,6 +599,8 @@ "Search studio conversations...": "スタジオの会話を検索...", "Existing studio conversations": "既存のスタジオの会話", "{{count}} context files used": "{{count}}コンテキストファイルが使用されます", + "{{count}} context files used_one": "{{count}}コンテキストファイルが使用されます", + "{{count}} context files used_other": "{{count}}コンテキストファイルが使用されます", "Add file to studio": "スタジオにファイルを追加します", "Close all tabs": "すべてのタブを閉じます", "Create ranges": "範囲を作成します", @@ -622,5 +624,15 @@ "Search templates...": "テンプレートを検索...", "Manage templates": "テンプレートを管理します", "Prompts": "プロンプト", - "Manage docs": "ドキュメントを管理します" + "Manage docs": "ドキュメントを管理します", + "Add doc to studio": "スタジオにドキュメントを追加します", + "Whole page": "全てのページ", + "Select sections": "セクションを選択します", + "Edit sections": "編集セクション", + "Documentation in studio": "スタジオでのドキュメント", + "# selected section_one": "選択された {{count}} つのセクション", + "# selected section_other": "選択された {{count}} つのセクション", + "Remove from studio": "スタジオから取り出します", + "Remove file from code studio context": "コードスタジオコンテキストからファイルを削除します", + "Remove page from code studio context": "コードスタジオコンテキストからページを削除します" } \ No newline at end of file diff --git a/client/src/locales/zh-CN.json b/client/src/locales/zh-CN.json index 71e080a7da..0440190918 100644 --- a/client/src/locales/zh-CN.json +++ b/client/src/locales/zh-CN.json @@ -607,6 +607,8 @@ "Add to studio": "添加到工作室", "Add file to code studio context": "将文件添加到代码工作室上下文", "{{count}} context files used": "{{count}}使用的上下文文件", + "{{count}} context files used_one": "{{count}}使用的上下文文件", + "{{count}} context files used_other": "{{count}}使用的上下文文件", "Add file to studio": "将文件添加到工作室", "Search studio conversations...": "搜索工作室对话...", "Close all tabs": "关闭所有选项卡", @@ -634,5 +636,15 @@ "Search templates...": "搜索模板...", "Manage templates": "管理模板", "Prompts": "提示", - "Manage docs": "管理文档" + "Manage docs": "管理文档", + "Add doc to studio": "将DOC添加到工作室", + "Whole page": "整页", + "Select sections": "选择部分", + "Edit sections": "编辑章节", + "Documentation in studio": "Studio中的文档", + "# selected section": "{{count}} 个选定部分", + "# selected section_one": "{{count}} 个选定部分", + "# selected section_other": "{{count}} 个选定部分", + "Remove file from code studio context": "从代码工作室上下文中删除文件", + "Remove page from code studio context": "从代码工作室上下文中删除页面" } \ No newline at end of file diff --git a/client/src/types/api.ts b/client/src/types/api.ts index 2f78e01cdf..af2a170cf9 100644 --- a/client/src/types/api.ts +++ b/client/src/types/api.ts @@ -376,7 +376,7 @@ export type DocShortType = { }; export type DocPageType = { - doc_id: number; + doc_id: string; doc_source: string; relative_url: string; absolute_url: string; diff --git a/client/src/types/general.ts b/client/src/types/general.ts index def8412da7..0b982fdbaa 100644 --- a/client/src/types/general.ts +++ b/client/src/types/general.ts @@ -92,6 +92,7 @@ export enum TabTypesEnum { FILE = 'file', CHAT = 'chat', STUDIO = 'studio', + DOC = 'doc', } export type FileTabType = { @@ -128,7 +129,19 @@ export type StudioTabType = { title?: string; }; -export type TabType = FileTabType | ChatTabType | StudioTabType; +export type DocTabType = { + type: TabTypesEnum.DOC; + key: string; + docId: string; + title?: string; + favicon?: string; + relativeUrl: string; + studioId?: string; + initialSections?: string[]; + isDocInContext?: boolean; +}; + +export type TabType = FileTabType | ChatTabType | StudioTabType | DocTabType; export type DraggableTabItem = { id: string; @@ -491,9 +504,9 @@ export type CommandBarStepType = { }; export type CommandBarActiveStepType = - | AddFileToStudioStepType + | AddToStudioStepType | { - id: Exclude; + id: Exclude; data?: Record; }; @@ -503,9 +516,16 @@ export type AddFileToStudioDataType = { branch?: string | null; }; -export type AddFileToStudioStepType = { - id: CommandBarStepEnum.ADD_FILE_TO_STUDIO; - data: AddFileToStudioDataType; +export type AddDocToStudioDataType = { + docId: string; + relativeUrl: string; + title?: string; + favicon?: string; +}; + +export type AddToStudioStepType = { + id: CommandBarStepEnum.ADD_TO_STUDIO; + data: AddFileToStudioDataType | AddDocToStudioDataType; }; export enum CommandBarStepEnum { @@ -520,7 +540,7 @@ export enum CommandBarStepEnum { CREATE_PROJECT = 'create_project', TOGGLE_THEME = 'toggle_theme', SEARCH_FILES = 'search_files', - ADD_FILE_TO_STUDIO = 'add_file_to_studio', + ADD_TO_STUDIO = 'add_to_studio', } export enum SettingSections {