mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-04 09:38:50 +01:00
🪟 fix+feat: General UI Enhancements (#2619)
* feat: Minor design changes to mimic OpenAI's latest login page * fix: Optimize ThemeSelector for mobile * fix: Use a svg for the logo for transperency in dark mode * feat: Update styles for Registration * feat: Update error colors for login & registration * fix: remove medium font * wip: Dropdown menu * feat: Update dropdown to match ChatGPT * feat: Improve rounding and padding * feat: Add UI Updates to RequestPasswordReset, PasswordRest and increase width for theme dropdown * fix: Modify the My Files modal's width to not touch the screen * feat: fix scrolling for dropdown, and make border width lighter * feat: Match popup menu design to OpenAI (p1/2) * fix+feat: fix dark mode, add user email, add lighter borders * fix: Add border color on focus of chat input. * feat: Move Export Conversation to a seperate button (testing) * fix: Properly center Login, Registration, Reset Password Flow * fix: Border colors on dark mode for settings modal * feat: Improve wording for settings menu * fix: Optimize settings modal for mobile and fix height for modal * feat: Optimize for desktop * fix: make TooltipTrigger asChild of button, improve settings mobile responsiveness * feat: Handle dropdowns properly TODO: Make height dynamic, fix dark mode colors * fix: input styles fix: make endpoint icon smaller * feat: Update UI to Match ChatGPT Style - Updated the dropdown styles to match the aesthetic of ChatGPT. - Decreased spacing within the conversation area for cleanliness. - Replaced the current archive icon with the ChatGPT's icon. * fix: fix colors for EditMenuButton & ArchiveButton for dark mode and light mode * fix: ui fixes * fix: Fix Conversation UI Bugs * fix: transparency of HoverToggle to make buttons not visible * fix: dark mode HoverToggle & compress menu item spacing * fix: responsiveness of export icon * fix: first mentionitem is set to always be highlighted * fix: improve hover state to text instead of bg * feat: Update icons to ChatGPT Style * fix: dark mode hover for PanelFileCell * fix: change navlinks z-index to 100 * fix: hover states for DataTable * feat: Move ExportButton to seperate component * chore: remove unused imports
This commit is contained in:
parent
d73ea8e1f2
commit
8f20fb28e5
43 changed files with 716 additions and 469 deletions
|
|
@ -9,7 +9,7 @@ const Logout = forwardRef(() => {
|
|||
|
||||
return (
|
||||
<button
|
||||
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||
className="group group flex w-full cursor-pointer items-center gap-2 rounded p-2.5 text-sm transition-colors duration-200 hover:bg-gray-500/10 focus:ring-0 dark:text-white dark:hover:bg-gray-600"
|
||||
onClick={() => logout()}
|
||||
>
|
||||
<LogOutIcon />
|
||||
|
|
|
|||
|
|
@ -6,17 +6,21 @@ interface Props {
|
|||
text: string;
|
||||
clickHandler?: () => void;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const NavLink: FC<Props> = forwardRef<HTMLAnchorElement, Props>((props, ref) => {
|
||||
const { svg, text, clickHandler, className = '' } = props;
|
||||
const { svg, text, clickHandler, disabled, className = '' } = props;
|
||||
const defaultProps: {
|
||||
className: string;
|
||||
onClick?: () => void;
|
||||
} = {
|
||||
className: cn(
|
||||
'flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10',
|
||||
'flex gap-2 rounded p-2.5 text-sm cursor-pointer focus:ring-0 group items-center transition-colors duration-200 hover:bg-gray-500/10 dark:text-white dark:hover:bg-gray-600',
|
||||
className,
|
||||
{
|
||||
'opacity-50 pointer-events-none': disabled,
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useLocation } from 'react-router-dom';
|
||||
import { Fragment, useState, memo } from 'react';
|
||||
import { Download, FileText } from 'lucide-react';
|
||||
import { FileText } from 'lucide-react';
|
||||
import { Menu, Transition } from '@headlessui/react';
|
||||
import { useRecoilValue, useRecoilState } from 'recoil';
|
||||
import { useGetUserBalance, useGetStartupConfig } from 'librechat-data-provider/react-query';
|
||||
|
|
@ -8,7 +8,6 @@ import type { TConversation } from 'librechat-data-provider';
|
|||
import FilesView from '~/components/Chat/Input/Files/FilesView';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
import useAvatar from '~/hooks/Messages/useAvatar';
|
||||
import { ExportModal } from './ExportConversation';
|
||||
import { LinkIcon, GearIcon } from '~/components';
|
||||
import { UserIcon } from '~/components/svg';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
|
@ -26,7 +25,6 @@ function NavLinks() {
|
|||
const balanceQuery = useGetUserBalance({
|
||||
enabled: !!isAuthenticated && startupConfig?.checkBalance,
|
||||
});
|
||||
const [showExports, setShowExports] = useState(false);
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
const [showFiles, setShowFiles] = useRecoilState(store.showFiles);
|
||||
|
||||
|
|
@ -42,34 +40,15 @@ function NavLinks() {
|
|||
conversation = activeConvo;
|
||||
}
|
||||
|
||||
const exportable =
|
||||
conversation &&
|
||||
conversation.conversationId &&
|
||||
conversation.conversationId !== 'new' &&
|
||||
conversation.conversationId !== 'search';
|
||||
|
||||
const clickHandler = () => {
|
||||
if (exportable) {
|
||||
setShowExports(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu as="div" className="group relative">
|
||||
{({ open }) => (
|
||||
<>
|
||||
{startupConfig?.checkBalance &&
|
||||
balanceQuery.data &&
|
||||
!isNaN(parseFloat(balanceQuery.data)) && (
|
||||
<div className="m-1 ml-3 whitespace-nowrap text-left text-sm text-black dark:text-gray-200">
|
||||
{`Balance: ${parseFloat(balanceQuery.data).toFixed(2)}`}
|
||||
</div>
|
||||
)}
|
||||
<Menu.Button
|
||||
className={cn(
|
||||
'group-ui-open:bg-gray-100 dark:group-ui-open:bg-gray-700 duration-350 mt-text-sm mb-1 flex h-11 w-full items-center gap-2 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-gray-100 dark:hover:bg-gray-700',
|
||||
open ? 'bg-gray-100 dark:bg-gray-700' : '',
|
||||
'group-ui-open:bg-gray-100 dark:group-ui-open:bg-gray-700 duration-350 mt-text-sm flex h-auto w-full items-center gap-2 rounded-lg p-2 text-sm transition-colors hover:bg-gray-100 dark:hover:bg-gray-800',
|
||||
open ? 'bg-gray-100 dark:bg-gray-800' : '',
|
||||
)}
|
||||
data-testid="nav-user"
|
||||
>
|
||||
|
|
@ -93,7 +72,7 @@ function NavLinks() {
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="mt-2 grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-black dark:text-white"
|
||||
className="mt-2 grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-black dark:text-gray-100"
|
||||
style={{ marginTop: '0', marginLeft: '0' }}
|
||||
>
|
||||
{user?.name || user?.username || localize('com_nav_user')}
|
||||
|
|
@ -109,24 +88,23 @@ function NavLinks() {
|
|||
leaveFrom="translate-y-0 opacity-100"
|
||||
leaveTo="translate-y-2 opacity-0"
|
||||
>
|
||||
<Menu.Items className="absolute bottom-full left-0 z-20 mb-1 mt-1 w-full translate-y-0 overflow-hidden rounded-lg bg-white py-1.5 opacity-100 outline-none dark:bg-gray-800">
|
||||
<Menu.Items className="absolute bottom-full left-0 z-[100] mb-1 mt-1 w-full translate-y-0 overflow-hidden rounded-lg border border-gray-300 bg-white p-1.5 opacity-100 shadow-lg outline-none dark:border-gray-600 dark:bg-gray-700">
|
||||
<div className="text-token-text-secondary ml-3 mr-2 py-2 text-sm" role="none">
|
||||
{user?.email || localize('com_nav_user')}
|
||||
</div>
|
||||
<div className="my-1.5 h-px bg-black/10 dark:bg-white/10" role="none" />
|
||||
{startupConfig?.checkBalance &&
|
||||
balanceQuery.data &&
|
||||
!isNaN(parseFloat(balanceQuery.data)) && (
|
||||
<>
|
||||
<div className="text-token-text-secondary ml-3 mr-2 py-2 text-sm">
|
||||
{`Balance: ${parseFloat(balanceQuery.data).toFixed(2)}`}
|
||||
</div>
|
||||
<div className="my-1.5 h-px bg-black/10 dark:bg-white/10" role="none" />
|
||||
</>
|
||||
)}
|
||||
<Menu.Item as="div">
|
||||
<NavLink
|
||||
className={cn(
|
||||
'flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700',
|
||||
exportable
|
||||
? 'cursor-pointer text-black dark:text-white'
|
||||
: 'cursor-not-allowed text-black/50 dark:text-white/50',
|
||||
)}
|
||||
svg={() => <Download size={16} />}
|
||||
text={localize('com_nav_export_conversation')}
|
||||
clickHandler={clickHandler}
|
||||
/>
|
||||
</Menu.Item>
|
||||
<div className="my-1 h-px bg-black/20 dark:bg-white/20" role="none" />
|
||||
<Menu.Item as="div">
|
||||
<NavLink
|
||||
className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||
svg={() => <FileText className="icon-md" />}
|
||||
text={localize('com_nav_my_files')}
|
||||
clickHandler={() => setShowFiles(true)}
|
||||
|
|
@ -135,7 +113,6 @@ function NavLinks() {
|
|||
{startupConfig?.helpAndFaqURL !== '/' && (
|
||||
<Menu.Item as="div">
|
||||
<NavLink
|
||||
className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||
svg={() => <LinkIcon />}
|
||||
text={localize('com_nav_help_faq')}
|
||||
clickHandler={() => window.open(startupConfig?.helpAndFaqURL, '_blank')}
|
||||
|
|
@ -144,13 +121,12 @@ function NavLinks() {
|
|||
)}
|
||||
<Menu.Item as="div">
|
||||
<NavLink
|
||||
className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||
svg={() => <GearIcon className="icon-md" />}
|
||||
text={localize('com_nav_settings')}
|
||||
clickHandler={() => setShowSettings(true)}
|
||||
/>
|
||||
</Menu.Item>
|
||||
<div className="my-1 h-px bg-black/20 bg-white/20" role="none" />
|
||||
<div className="my-1.5 h-px bg-black/10 dark:bg-white/10" role="none" />
|
||||
<Menu.Item as="div">
|
||||
<Logout />
|
||||
</Menu.Item>
|
||||
|
|
@ -159,9 +135,6 @@ function NavLinks() {
|
|||
</>
|
||||
)}
|
||||
</Menu>
|
||||
{showExports && (
|
||||
<ExportModal open={showExports} onOpenChange={setShowExports} conversation={conversation} />
|
||||
)}
|
||||
{showFiles && <FilesView open={showFiles} onOpenChange={setShowFiles} />}
|
||||
{showSettings && <Settings open={showSettings} onOpenChange={setShowSettings} />}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent
|
||||
className={cn(
|
||||
'shadow-2xl md:min-h-[373px] md:w-[680px]',
|
||||
isSmallScreen ? 'top-20 -translate-y-0' : '',
|
||||
'overflow-hidden shadow-2xl md:min-h-[373px] md:w-[680px]',
|
||||
isSmallScreen ? 'top-5 -translate-y-0' : '',
|
||||
)}
|
||||
>
|
||||
<DialogHeader>
|
||||
|
|
@ -25,19 +25,19 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
{localize('com_nav_settings')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="px-6">
|
||||
<div className="max-h-[373px] overflow-auto px-6 md:min-h-[373px] md:w-[680px]">
|
||||
<Tabs.Root
|
||||
defaultValue={SettingsTabValues.GENERAL}
|
||||
className="flex flex-col gap-10 md:flex-row"
|
||||
orientation="vertical"
|
||||
orientation="horizontal"
|
||||
>
|
||||
<Tabs.List
|
||||
aria-label="Settings"
|
||||
role="tablist"
|
||||
aria-orientation="vertical"
|
||||
aria-orientation="horizontal"
|
||||
className={cn(
|
||||
'min-w-auto -ml-[8px] flex flex-shrink-0 flex-col',
|
||||
isSmallScreen ? 'flex-row rounded-lg bg-gray-200 p-1 dark:bg-gray-700' : '',
|
||||
'min-w-auto max-w-auto -ml-[8px] flex flex-shrink-0 flex-col flex-wrap overflow-auto sm:max-w-none',
|
||||
isSmallScreen ? 'flex-row rounded-lg bg-gray-200 p-1 dark:bg-gray-800' : '',
|
||||
)}
|
||||
style={{ outline: 'none' }}
|
||||
>
|
||||
|
|
@ -112,11 +112,13 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
{localize('com_nav_setting_account')}
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<General />
|
||||
<Messages />
|
||||
<Beta />
|
||||
<Data />
|
||||
<Account />
|
||||
<div className="h-screen max-h-[373px] overflow-auto sm:w-full sm:max-w-none">
|
||||
<General />
|
||||
<Messages />
|
||||
<Beta />
|
||||
<Data />
|
||||
<Account />
|
||||
</div>
|
||||
</Tabs.Root>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ function Account({ onCheckedChange }: { onCheckedChange?: (value: boolean) => vo
|
|||
<Tabs.Content
|
||||
value={SettingsTabValues.ACCOUNT}
|
||||
role="tabpanel"
|
||||
className="w-full md:min-h-[300px]"
|
||||
className="w-full md:min-h-[271px]"
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<Avatar />
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
|
|
@ -39,7 +39,7 @@ function Account({ onCheckedChange }: { onCheckedChange?: (value: boolean) => vo
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700"></div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600"></div>
|
||||
</Tabs.Content>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ function Beta() {
|
|||
<Tabs.Content
|
||||
value={SettingsTabValues.BETA}
|
||||
role="tabpanel"
|
||||
className="w-full md:min-h-[300px]"
|
||||
className="w-full md:min-h-[271px]"
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<ModularChat />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<LaTeXParsing />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -100,18 +100,18 @@ function Data() {
|
|||
<Tabs.Content
|
||||
value={SettingsTabValues.DATA}
|
||||
role="tabpanel"
|
||||
className="w-full md:min-h-[300px]"
|
||||
className="w-full md:min-h-[271px]"
|
||||
ref={dataTabRef}
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<ImportConversations />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<RevokeKeysButton all={true} />
|
||||
</div>
|
||||
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<ClearChatsButton
|
||||
confirmClear={confirmClearConvos}
|
||||
onClick={clearConvos}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,9 @@ export const ThemeSelector = ({
|
|||
value={theme}
|
||||
onChange={onChange}
|
||||
options={themeOptions}
|
||||
width={150}
|
||||
width={220}
|
||||
position={'left'}
|
||||
maxHeight="200px"
|
||||
testId="theme-selector"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -103,7 +105,13 @@ export const LangSelector = ({
|
|||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_language')} </div>
|
||||
<Dropdown value={langcode} onChange={onChange} options={languageOptions} />
|
||||
<Dropdown
|
||||
value={langcode}
|
||||
onChange={onChange}
|
||||
position={'left'}
|
||||
maxHeight="271px"
|
||||
options={languageOptions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -142,26 +150,26 @@ function General() {
|
|||
<Tabs.Content
|
||||
value={SettingsTabValues.GENERAL}
|
||||
role="tabpanel"
|
||||
className="w-full md:min-h-[300px]"
|
||||
className="w-full md:min-h-[271px]"
|
||||
ref={contentRef}
|
||||
>
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<ThemeSelector theme={theme} onChange={changeTheme} />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<LangSelector langcode={selectedLang} onChange={changeLang} />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<AutoScrollSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<HideSidePanelSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<ArchivedChats />
|
||||
</div>
|
||||
{/* <div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
{/* <div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
</div> */}
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export const ForkSettings = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_ui_fork_change_default')} </div>
|
||||
<Dropdown
|
||||
|
|
@ -26,11 +26,13 @@ export const ForkSettings = () => {
|
|||
onChange={setForkSetting}
|
||||
options={forkOptions}
|
||||
width={200}
|
||||
position={'left'}
|
||||
maxHeight="199px"
|
||||
testId="fork-setting-dropdown"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_ui_fork_default')} </div>
|
||||
<Switch
|
||||
|
|
@ -42,7 +44,7 @@ export const ForkSettings = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_ui_fork_split_target_setting')} </div>
|
||||
<Switch
|
||||
|
|
|
|||
|
|
@ -7,16 +7,12 @@ import { ForkSettings } from './ForkSettings';
|
|||
|
||||
function Messages() {
|
||||
return (
|
||||
<Tabs.Content
|
||||
value={SettingsTabValues.MESSAGES}
|
||||
role="tabpanel"
|
||||
className="w-full md:min-h-[300px]"
|
||||
>
|
||||
<Tabs.Content value={SettingsTabValues.MESSAGES} role="tabpanel" className="md: w-full">
|
||||
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<SendMessageKeyEnter />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<ShowCodeSwitch />
|
||||
</div>
|
||||
<ForkSettings />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue