make all chat area components tabbable (accessibility) (#246)
* make all chat area components tabbable * align message role description * remove inline styles on icons * remove inline styles on icons
This commit is contained in:
parent
5d31947ab9
commit
a78a8c4a94
|
@ -217,16 +217,17 @@ export const Chat: FC<Props> = memo(
|
||||||
<>
|
<>
|
||||||
<div className="flex justify-center border border-b-neutral-300 bg-neutral-100 py-2 text-sm text-neutral-500 dark:border-none dark:bg-[#444654] dark:text-neutral-200">
|
<div className="flex justify-center border border-b-neutral-300 bg-neutral-100 py-2 text-sm text-neutral-500 dark:border-none dark:bg-[#444654] dark:text-neutral-200">
|
||||||
{t('Model')}: {conversation.model.name}
|
{t('Model')}: {conversation.model.name}
|
||||||
<IconSettings
|
<button
|
||||||
className="ml-2 cursor-pointer hover:opacity-50"
|
className="ml-2 cursor-pointer hover:opacity-50"
|
||||||
onClick={handleSettings}
|
onClick={handleSettings}
|
||||||
size={18}
|
>
|
||||||
/>
|
<IconSettings size={18} />
|
||||||
<IconClearAll
|
</button>
|
||||||
|
<button
|
||||||
className="ml-2 cursor-pointer hover:opacity-50"
|
className="ml-2 cursor-pointer hover:opacity-50"
|
||||||
onClick={onClearAll}
|
onClick={onClearAll}>
|
||||||
size={18}
|
<IconClearAll size={18} />
|
||||||
/>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{showSettings && (
|
{showSettings && (
|
||||||
<div className="flex flex-col space-y-10 md:mx-auto md:max-w-xl md:gap-6 md:py-3 md:pt-6 lg:max-w-2xl lg:px-0 xl:max-w-3xl">
|
<div className="flex flex-col space-y-10 md:mx-auto md:max-w-xl md:gap-6 md:py-3 md:pt-6 lg:max-w-2xl lg:px-0 xl:max-w-3xl">
|
||||||
|
|
|
@ -237,28 +237,26 @@ export const ChatInput: FC<Props> = ({
|
||||||
<div className="stretch mx-2 mt-4 flex flex-row gap-3 last:mb-2 md:mx-4 md:mt-[52px] md:last:mb-6 lg:mx-auto lg:max-w-3xl">
|
<div className="stretch mx-2 mt-4 flex flex-row gap-3 last:mb-2 md:mx-4 md:mt-[52px] md:last:mb-6 lg:mx-auto lg:max-w-3xl">
|
||||||
{messageIsStreaming && (
|
{messageIsStreaming && (
|
||||||
<button
|
<button
|
||||||
className="absolute top-2 left-0 right-0 mx-auto mt-2 w-fit rounded border border-neutral-200 bg-white py-2 px-4 text-black hover:opacity-50 dark:border-neutral-600 dark:bg-[#343541] dark:text-white md:top-0"
|
className="absolute left-0 right-0 mx-auto mt-2 flex w-fit items-center gap-3 rounded border border-neutral-200 bg-white py-2 px-4 text-black hover:opacity-50 dark:border-neutral-600 dark:bg-[#343541] dark:text-white md:top-0"
|
||||||
onClick={handleStopConversation}
|
onClick={handleStopConversation}
|
||||||
>
|
>
|
||||||
<IconPlayerStop size={16} className="mb-[2px] inline-block" />{' '}
|
<IconPlayerStop size={16} /> {t('Stop Generating')}
|
||||||
{t('Stop Generating')}
|
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!messageIsStreaming && !conversationIsEmpty && (
|
{!messageIsStreaming && !conversationIsEmpty && (
|
||||||
<button
|
<button
|
||||||
className="absolute left-0 right-0 mx-auto mt-2 w-fit rounded border border-neutral-200 bg-white py-2 px-4 text-black hover:opacity-50 dark:border-neutral-600 dark:bg-[#343541] dark:text-white md:top-0"
|
className="absolute left-0 right-0 mx-auto mt-2 flex w-fit items-center gap-3 rounded border border-neutral-200 bg-white py-2 px-4 text-black hover:opacity-50 dark:border-neutral-600 dark:bg-[#343541] dark:text-white md:top-0"
|
||||||
onClick={onRegenerate}
|
onClick={onRegenerate}
|
||||||
>
|
>
|
||||||
<IconRepeat size={16} className="mb-[2px] inline-block" />{' '}
|
<IconRepeat size={16} /> {t('Regenerate response')}
|
||||||
{t('Regenerate response')}
|
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="relative mx-2 flex w-full flex-grow flex-col rounded-md border border-black/10 bg-white py-2 shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:border-gray-900/50 dark:bg-[#40414F] dark:text-white dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] sm:mx-4 md:py-3 md:pl-4">
|
<div className="relative mx-2 flex w-full flex-grow flex-col rounded-md border border-black/10 bg-white shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:border-gray-900/50 dark:bg-[#40414F] dark:text-white dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] sm:mx-4">
|
||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
className="m-0 w-full resize-none border-0 bg-transparent p-0 pr-8 pl-2 text-black outline-none focus:ring-0 focus-visible:ring-0 dark:bg-transparent dark:text-white md:pl-0"
|
className="m-0 w-full resize-none border-0 bg-transparent p-0 pr-8 pl-2 text-black dark:bg-transparent dark:text-white py-2 md:py-3 md:pl-4"
|
||||||
style={{
|
style={{
|
||||||
resize: 'none',
|
resize: 'none',
|
||||||
bottom: `${textareaRef?.current?.scrollHeight}px`,
|
bottom: `${textareaRef?.current?.scrollHeight}px`,
|
||||||
|
@ -279,12 +277,11 @@ export const ChatInput: FC<Props> = ({
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="absolute right-3 rounded-sm p-1 text-neutral-800 hover:bg-neutral-200 hover:text-neutral-900 focus:outline-none dark:bg-opacity-50 dark:text-neutral-100 dark:hover:text-neutral-200"
|
className="absolute right-2 top-2 rounded-sm p-1 text-neutral-800 hover:bg-neutral-200 hover:text-neutral-900 dark:bg-opacity-50 dark:text-neutral-100 dark:hover:text-neutral-200 opacity-60"
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
>
|
>
|
||||||
<IconSend size={16} className="opacity-60" />
|
<IconSend size={18} />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{showPromptList && prompts.length > 0 && (
|
{showPromptList && prompts.length > 0 && (
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { Message } from '@/types/chat';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { FC, memo, useEffect, useRef, useState } from 'react';
|
import { FC, memo, useEffect, useRef, useState } from 'react';
|
||||||
|
import { IconCheck, IconCopy } from '@tabler/icons-react';
|
||||||
import rehypeMathjax from 'rehype-mathjax';
|
import rehypeMathjax from 'rehype-mathjax';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
import remarkMath from 'remark-math';
|
import remarkMath from 'remark-math';
|
||||||
import { CodeBlock } from '../Markdown/CodeBlock';
|
import { CodeBlock } from '../Markdown/CodeBlock';
|
||||||
import { MemoizedReactMarkdown } from '../Markdown/MemoizedReactMarkdown';
|
import { MemoizedReactMarkdown } from '../Markdown/MemoizedReactMarkdown';
|
||||||
import { CopyButton } from './CopyButton';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
message: Message;
|
message: Message;
|
||||||
|
@ -19,7 +19,6 @@ export const ChatMessage: FC<Props> = memo(
|
||||||
({ message, messageIndex, onEditMessage }) => {
|
({ message, messageIndex, onEditMessage }) => {
|
||||||
const { t } = useTranslation('chat');
|
const { t } = useTranslation('chat');
|
||||||
const [isEditing, setIsEditing] = useState<boolean>(false);
|
const [isEditing, setIsEditing] = useState<boolean>(false);
|
||||||
const [isHovering, setIsHovering] = useState<boolean>(false);
|
|
||||||
const [messageContent, setMessageContent] = useState(message.content);
|
const [messageContent, setMessageContent] = useState(message.content);
|
||||||
const [messagedCopied, setMessageCopied] = useState(false);
|
const [messagedCopied, setMessageCopied] = useState(false);
|
||||||
|
|
||||||
|
@ -79,11 +78,9 @@ export const ChatMessage: FC<Props> = memo(
|
||||||
: 'border-b border-black/10 bg-white text-gray-800 dark:border-gray-900/50 dark:bg-[#343541] dark:text-gray-100'
|
: 'border-b border-black/10 bg-white text-gray-800 dark:border-gray-900/50 dark:bg-[#343541] dark:text-gray-100'
|
||||||
}`}
|
}`}
|
||||||
style={{ overflowWrap: 'anywhere' }}
|
style={{ overflowWrap: 'anywhere' }}
|
||||||
onMouseEnter={() => setIsHovering(true)}
|
|
||||||
onMouseLeave={() => setIsHovering(false)}
|
|
||||||
>
|
>
|
||||||
<div className="relative m-auto flex gap-4 p-4 text-base md:max-w-2xl md:gap-6 md:py-6 lg:max-w-2xl lg:px-0 xl:max-w-3xl">
|
<div className="relative m-auto flex gap-4 p-4 text-base md:max-w-2xl md:gap-6 md:py-6 lg:max-w-2xl lg:px-0 xl:max-w-3xl">
|
||||||
<div className="min-w-[40px] font-bold">
|
<div className="min-w-[40px] font-bold text-right">
|
||||||
{message.role === 'assistant' ? t('AI') : t('You')}:
|
{message.role === 'assistant' ? t('AI') : t('You')}:
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -94,7 +91,7 @@ export const ChatMessage: FC<Props> = memo(
|
||||||
<div className="flex w-full flex-col">
|
<div className="flex w-full flex-col">
|
||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
className="w-full resize-none whitespace-pre-wrap border-none outline-none dark:bg-[#343541]"
|
className="w-full resize-none whitespace-pre-wrap border-none dark:bg-[#343541]"
|
||||||
value={messageContent}
|
value={messageContent}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
onKeyDown={handlePressEnter}
|
onKeyDown={handlePressEnter}
|
||||||
|
@ -133,24 +130,44 @@ export const ChatMessage: FC<Props> = memo(
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(isHovering || window.innerWidth < 640) && !isEditing && (
|
{(window.innerWidth < 640 || !isEditing) && (
|
||||||
<button
|
<button
|
||||||
className={`absolute ${
|
className={`absolute translate-x-[1000px] text-gray-500 hover:text-gray-700 focus:translate-x-0 group-hover:translate-x-0 dark:text-gray-400 dark:hover:text-gray-300 ${
|
||||||
window.innerWidth < 640
|
window.innerWidth < 640
|
||||||
? 'right-3 bottom-1'
|
? 'right-3 bottom-1'
|
||||||
: 'right-0 top-[26px]'
|
: 'right-0 top-[26px]'
|
||||||
}`}
|
}
|
||||||
|
`}
|
||||||
|
onClick={toggleEditing}
|
||||||
>
|
>
|
||||||
<IconEdit
|
<IconEdit size={20} />
|
||||||
size={20}
|
|
||||||
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
|
|
||||||
onClick={toggleEditing}
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
<div
|
||||||
|
className={`absolute ${
|
||||||
|
window.innerWidth < 640
|
||||||
|
? 'right-3 bottom-1'
|
||||||
|
: 'right-0 top-[26px] m-0'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{messagedCopied ? (
|
||||||
|
<IconCheck
|
||||||
|
size={20}
|
||||||
|
className="text-green-500 dark:text-green-400"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
className="translate-x-[1000px] text-gray-500 hover:text-gray-700 focus:translate-x-0 group-hover:translate-x-0 dark:text-gray-400 dark:hover:text-gray-300"
|
||||||
|
onClick={copyOnClick}
|
||||||
|
>
|
||||||
|
<IconCopy size={20} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<MemoizedReactMarkdown
|
<MemoizedReactMarkdown
|
||||||
className="prose dark:prose-invert"
|
className="prose dark:prose-invert"
|
||||||
remarkPlugins={[remarkGfm, remarkMath]}
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
|
@ -197,13 +214,6 @@ export const ChatMessage: FC<Props> = memo(
|
||||||
>
|
>
|
||||||
{message.content}
|
{message.content}
|
||||||
</MemoizedReactMarkdown>
|
</MemoizedReactMarkdown>
|
||||||
|
|
||||||
{(isHovering || window.innerWidth < 640) && (
|
|
||||||
<CopyButton
|
|
||||||
messagedCopied={messagedCopied}
|
|
||||||
copyOnClick={copyOnClick}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { IconCheck, IconCopy } from '@tabler/icons-react';
|
|
||||||
import { FC } from 'react';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
messagedCopied: boolean;
|
|
||||||
copyOnClick: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CopyButton: FC<Props> = ({ messagedCopied, copyOnClick }) => (
|
|
||||||
<button
|
|
||||||
className={`absolute ${
|
|
||||||
window.innerWidth < 640 ? 'right-3 bottom-1' : 'right-0 top-[26px] m-0'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{messagedCopied ? (
|
|
||||||
<IconCheck size={20} className="text-green-500 dark:text-green-400" />
|
|
||||||
) : (
|
|
||||||
<IconCopy
|
|
||||||
size={20}
|
|
||||||
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
|
|
||||||
onClick={copyOnClick}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
);
|
|
|
@ -17,7 +17,7 @@ export const ModelSelect: FC<Props> = ({ model, models, onModelChange }) => {
|
||||||
</label>
|
</label>
|
||||||
<div className="w-full rounded-lg border border-neutral-200 bg-transparent pr-2 text-neutral-900 dark:border-neutral-600 dark:text-white">
|
<div className="w-full rounded-lg border border-neutral-200 bg-transparent pr-2 text-neutral-900 dark:border-neutral-600 dark:text-white">
|
||||||
<select
|
<select
|
||||||
className="w-full bg-transparent p-2 outline-0"
|
className="w-full bg-transparent p-2"
|
||||||
placeholder={t('Select a model') || ''}
|
placeholder={t('Select a model') || ''}
|
||||||
value={model.id}
|
value={model.id}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
|
|
@ -19,13 +19,7 @@ export const PromptList: FC<Props> = ({
|
||||||
return (
|
return (
|
||||||
<ul
|
<ul
|
||||||
ref={promptListRef}
|
ref={promptListRef}
|
||||||
className="z-10 w-full rounded border border-black/10 bg-white shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:border-neutral-500 dark:bg-[#343541] dark:text-white dark:shadow-[0_0_15px_rgba(0,0,0,0.10)]"
|
className="z-10 w-full rounded border border-black/10 bg-white shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:border-neutral-500 dark:bg-[#343541] dark:text-white dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] max-h-52 overflow-scroll"
|
||||||
style={{
|
|
||||||
width: 'calc(100% - 48px)',
|
|
||||||
bottom: '100%',
|
|
||||||
marginBottom: '4px',
|
|
||||||
maxHeight: '200px',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{prompts.map((prompt, index) => (
|
{prompts.map((prompt, index) => (
|
||||||
<li
|
<li
|
||||||
|
|
|
@ -14,10 +14,10 @@ export const Regenerate: FC<Props> = ({ onRegenerate }) => {
|
||||||
{t('Sorry, there was an error.')}
|
{t('Sorry, there was an error.')}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="flex h-12 w-full items-center justify-center rounded-lg border border-b-neutral-300 bg-neutral-100 text-sm font-semibold text-neutral-500 dark:border-none dark:bg-[#444654] dark:text-neutral-200"
|
className="flex h-12 gap-2 w-full items-center justify-center rounded-lg border border-b-neutral-300 bg-neutral-100 text-sm font-semibold text-neutral-500 dark:border-none dark:bg-[#444654] dark:text-neutral-200"
|
||||||
onClick={onRegenerate}
|
onClick={onRegenerate}
|
||||||
>
|
>
|
||||||
<IconRefresh className="mr-2" />
|
<IconRefresh />
|
||||||
<div>{t('Regenerate response')}</div>
|
<div>{t('Regenerate response')}</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -196,7 +196,7 @@ export const SystemPrompt: FC<Props> = ({
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
className="w-full rounded-lg border border-neutral-200 bg-transparent px-4 py-3 text-neutral-900 focus:outline-none dark:border-neutral-600 dark:text-neutral-100"
|
className="w-full rounded-lg border border-neutral-200 bg-transparent px-4 py-3 text-neutral-900 dark:border-neutral-600 dark:text-neutral-100"
|
||||||
style={{
|
style={{
|
||||||
resize: 'none',
|
resize: 'none',
|
||||||
bottom: `${textareaRef?.current?.scrollHeight}px`,
|
bottom: `${textareaRef?.current?.scrollHeight}px`,
|
||||||
|
|
|
@ -194,11 +194,9 @@ export const Chatbar: FC<Props> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="mt-8 select-none text-center text-white opacity-50">
|
<div className="flex flex-col gap-3 items-center text-sm leading-normal mt-8 text-white opacity-50">
|
||||||
<IconMessagesOff className="mx-auto mb-3" />
|
<IconMessagesOff />
|
||||||
<span className="text-[14px] leading-normal">
|
|
||||||
{t('No conversations.')}
|
{t('No conversations.')}
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -64,18 +64,18 @@ export const CodeBlock: FC<Props> = memo(({ language, value }) => {
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<button
|
<button
|
||||||
className="flex items-center rounded bg-none py-0.5 px-2 text-xs text-white focus:outline-none"
|
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-white"
|
||||||
onClick={copyToClipboard}
|
onClick={copyToClipboard}
|
||||||
>
|
>
|
||||||
{isCopied ? (
|
{isCopied ? (
|
||||||
<IconCheck size={18} className="mr-1.5" />
|
<IconCheck size={18} />
|
||||||
) : (
|
) : (
|
||||||
<IconClipboard size={18} className="mr-1.5" />
|
<IconClipboard size={18} />
|
||||||
)}
|
)}
|
||||||
{isCopied ? t('Copied!') : t('Copy code')}
|
{isCopied ? t('Copied!') : t('Copy code')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="flex items-center rounded bg-none py-0.5 pl-2 text-xs text-white focus:outline-none"
|
className="flex items-center rounded bg-none p-1 text-xs text-white"
|
||||||
onClick={downloadAsFile}
|
onClick={downloadAsFile}
|
||||||
>
|
>
|
||||||
<IconDownload size={18} />
|
<IconDownload size={18} />
|
||||||
|
|
|
@ -655,21 +655,24 @@ const Home: React.FC<HomeProps> = ({ serverSideApiKeyIsSet }) => {
|
||||||
onImportConversations={handleImportConversations}
|
onImportConversations={handleImportConversations}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IconArrowBarLeft
|
<button
|
||||||
className="fixed top-5 left-[270px] z-50 h-7 w-7 cursor-pointer hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:left-[270px] sm:h-8 sm:w-8 sm:text-neutral-700"
|
className="fixed top-5 left-[270px] z-50 h-7 w-7 hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:left-[270px] sm:h-8 sm:w-8 sm:text-neutral-700"
|
||||||
onClick={handleToggleChatbar}
|
onClick={handleToggleChatbar}
|
||||||
/>
|
>
|
||||||
|
<IconArrowBarLeft />
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
onClick={handleToggleChatbar}
|
onClick={handleToggleChatbar}
|
||||||
className="absolute top-0 left-0 z-10 h-full w-full bg-black opacity-70 sm:hidden"
|
className="absolute top-0 left-0 z-10 h-full w-full bg-black opacity-70 sm:hidden"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<IconArrowBarRight
|
<button
|
||||||
className="fixed top-2.5 left-4 z-50 h-7 w-7 cursor-pointer text-white hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:left-4 sm:h-8 sm:w-8 sm:text-neutral-700"
|
className="fixed top-2.5 left-4 z-50 h-7 w-7 text-white hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:left-4 sm:h-8 sm:w-8 sm:text-neutral-700"
|
||||||
onClick={handleToggleChatbar}
|
onClick={handleToggleChatbar}
|
||||||
/>
|
>
|
||||||
|
<IconArrowBarRight />
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-1">
|
<div className="flex flex-1">
|
||||||
|
@ -702,20 +705,24 @@ const Home: React.FC<HomeProps> = ({ serverSideApiKeyIsSet }) => {
|
||||||
onDeleteFolder={handleDeleteFolder}
|
onDeleteFolder={handleDeleteFolder}
|
||||||
onUpdateFolder={handleUpdateFolder}
|
onUpdateFolder={handleUpdateFolder}
|
||||||
/>
|
/>
|
||||||
<IconArrowBarRight
|
<button
|
||||||
className="fixed top-5 right-[270px] z-50 h-7 w-7 cursor-pointer hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:right-[270px] sm:h-8 sm:w-8 sm:text-neutral-700"
|
className="fixed top-5 right-[270px] z-50 h-7 w-7 hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:right-[270px] sm:h-8 sm:w-8 sm:text-neutral-700"
|
||||||
onClick={handleTogglePromptbar}
|
onClick={handleTogglePromptbar}
|
||||||
/>
|
>
|
||||||
|
<IconArrowBarRight />
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
onClick={handleTogglePromptbar}
|
onClick={handleTogglePromptbar}
|
||||||
className="absolute top-0 left-0 z-10 h-full w-full bg-black opacity-70 sm:hidden"
|
className="absolute top-0 left-0 z-10 h-full w-full bg-black opacity-70 sm:hidden"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<IconArrowBarLeft
|
<button
|
||||||
className="fixed top-2.5 right-4 z-50 h-7 w-7 cursor-pointer text-white hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:right-4 sm:h-8 sm:w-8 sm:text-neutral-700"
|
className="fixed top-2.5 right-4 z-50 h-7 w-7 text-white hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:right-4 sm:h-8 sm:w-8 sm:text-neutral-700"
|
||||||
onClick={handleTogglePromptbar}
|
onClick={handleTogglePromptbar}
|
||||||
/>
|
>
|
||||||
|
<IconArrowBarLeft />
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -9,5 +9,10 @@ module.exports = {
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
|
variants: {
|
||||||
|
extend: {
|
||||||
|
visibility: ["group-hover"],
|
||||||
|
},
|
||||||
|
},
|
||||||
plugins: [require('@tailwindcss/typography')],
|
plugins: [require('@tailwindcss/typography')],
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue