kirin-ri / memo

0 stars 0 forks source link

39 #11

Open kirin-ri opened 6 months ago

kirin-ri commented 6 months ago
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { IconFile } from '@tabler/icons-react';
import { CloseSidebarButton, OpenSidebarButton } from './components/OpenCloseButton';

interface Reference {
  id: string;
  title: string;
  description: string;
}

interface Props<T> {
  isOpen: boolean;
  side: 'left' | 'right';
  items: T[];
  toggleOpen: () => void;
}

const ReferenceSidebar = ({
  isOpen,
  side,
  items,
  toggleOpen,
}: Props<Reference>) => {
  const [hoveredItemId, setHoveredItemId] = useState<string | null>(null);
  const { t } = useTranslation('promptbar');

  const listItemStyle = (isHovered: boolean) => ({
    marginBottom: '10px',
    border: '1px solid white',
    padding: '10px',
    borderRadius: '5px',
    backgroundColor: isHovered ? '#575757' : '#343541', // Change background color on hover
    transition: 'background-color 0.3s',
  });

  return isOpen ? (
    <div className={`fixed top-0 ${side}-0 z-40 flex h-full w-[260px] flex-none flex-col space-y-2 bg-[#202123] p-2 text-[14px] transition-all sm:relative sm:top-0`}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
        <IconFile style={{ marginRight: '8px' }} />
        参照リスト
      </div>
      <ul>
        {items.map((item) => (
          <a href="#" target="_blank" rel="noopener noreferrer" key={item.id} style={{ textDecoration: 'none', color: 'white' }}>
            <li
              style={listItemStyle(item.id === hoveredItemId)}
              onMouseEnter={() => setHoveredItemId(item.id)}
              onMouseLeave={() => setHoveredItemId(null)}
            >
              <h3 style={{ marginBottom: '10px' }}>{item.title}</h3>
              <p>{item.description}</p>
            </li>
          </a>
        ))}
      </ul>
      <CloseSidebarButton onClick={toggleOpen} side={side} />
    </div>
  ) : (
    <OpenSidebarButton onClick={toggleOpen} side={side} />
  );
};

export default ReferenceSidebar;
kirin-ri commented 6 months ago
import { IconArrowBarLeft, IconArrowBarRight } from '@tabler/icons-react';

interface Props {
  onClick: any;
  side: 'left' | 'right';
}

export const CloseSidebarButton = ({ onClick, side }: Props) => {
  return (
    <>
      <button
        className={`fixed top-5 ${
          side === 'right' ? 'right-[270px]' : '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:${
          side === 'right' ? 'right-[270px]' : 'left-[270px]'
        } sm:h-8 sm:w-8 sm:text-neutral-700`}
        onClick={onClick}
      >
        {side === 'right' ? <IconArrowBarRight /> : <IconArrowBarLeft />}
      </button>
      <div
        onClick={onClick}
        className="absolute top-0 left-0 z-10 h-full w-full bg-black opacity-70 sm:hidden"
      ></div>
    </>
  );
};

export const OpenSidebarButton = ({ onClick, side }: Props) => {
  return (
    <button
      className={`fixed top-2.5 ${
        side === 'right' ? 'right-2' : 'left-2'
      } 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:${
        side === 'right' ? 'right-2' : 'left-2'
      } sm:h-8 sm:w-8 sm:text-neutral-700`}
      onClick={onClick}
    >
      {side === 'right' ? <IconArrowBarLeft /> : <IconArrowBarRight />}
    </button>
  );
};
kirin-ri commented 6 months ago
import { IconFolderPlus, IconMistOff, IconPlus, IconFile } from '@tabler/icons-react';
import { ReactNode, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { IconArrowBarLeft, IconArrowBarRight } from '@tabler/icons-react';

import {
  CloseSidebarButton,
  OpenSidebarButton,
} from './components/OpenCloseButton';

interface Reference {
  id: string;
  title: string;
  description: string;
}

interface Props<T> {
  isOpen: boolean;
  side: 'left' | 'right';
  items: T[];
  toggleOpen: () => void;
}

const ReferenceSidebar = <T,>({
  isOpen,
  side,
  items,
  toggleOpen,
}: Props<T>) => {
  const { t } = useTranslation('promptbar');
  const [hoveredItemId, setHoveredItemId] = useState<string | null>(null);

  const allowDrop = (e: any) => {
    e.preventDefault();
  };

  const highlightDrop = (e: any) => {
    e.target.style.background = '#343541';
  };

  const removeHighlight = (e: any) => {
    e.target.style.background = 'none';
  };

  const itemsample = [
    {id:'1',title:'実績発電原子炉に係る新規制基準の考え方について.pdf',description:'…見直しを行うために「発電用軽水型原子炉の新規制基準に関する検討チーム」(以下「検討チーム」という。)を組織し、発電用軽水型原子炉の新規制基準策定のための検討を開始した。検討チーム…'},
    {id:'2',title:'実績発電原子炉に係る新規制基準の考え方について.pdf',description:'…見直しを行うために「発電用軽水型原子炉の新規制基準に関する検討チーム」(以下「検討チーム」という。)を組織し、発電用軽水型原子炉の新規制基準策定のための検討を開始した。検討チーム…'},
    {id:'3',title:'実績発電原子炉に係る新規制基準の考え方について.pdf',description:'…見直しを行うために「発電用軽水型原子炉の新規制基準に関する検討チーム」(以下「検討チーム」という。)を組織し、発電用軽水型原子炉の新規制基準策定のための検討を開始した。検討チーム…'},
  ]

  const headerStyle = {
    display: 'flex',
    justiftContent: 'space-between',
    fontSize: '15px',
    marginBottom: '15px',
    width: '100%',
  }
  const titleIcon = {
    display: 'flex',
    marginRight: '8px',
  }
  const listItemStyle = (isHovered: boolean) => ({
    marginBottom: '10px',
    border: '1px solid white',
    padding: '10px',
    borderRadius: '5px',
    backgroundColor: isHovered ? '#0056b3' : '#343541', // Change background color on hover
    transition: 'background-color 0.3s',
  });
  const listTitleStyle = {
    marginBottom: '10px',
  }
  return isOpen ? (
    <div>
      <div className={`fixed top-0 ${side}-0 z-40 flex h-full w-[400px] flex-none flex-col space-y-2 bg-[#202123] p-2 text-[14px] transition-all sm:relative sm:top-0`}>
        <div style={headerStyle}>
          <div style={titleIcon}>
            <IconFile />
            参照リスト
            <CloseSidebarButton onClick={toggleOpen} side={side} />
          </div>
        </div>
        <ul>
          {itemsample.map((item) => (
            <a href="test" target="_blank" rel="noopener noreferrer">
              {/* <li
                key={item.id}
                className="p-2 bg-[#343541] rounded-md text-white"
                style={listItemStyle}
              > */}
              <li
              style={listItemStyle(item.id === hoveredItemId)}
              onMouseEnter={() => setHoveredItemId(item.id)}
              onMouseLeave={() => setHoveredItemId(null)}
              >
                <h3 style={listTitleStyle}>{item.title}</h3>
                <p>{item.description}</p>
              </li>
            </a>
          ))}
        </ul>
      </div>
    </div>
  ) : (
    <OpenSidebarButton onClick={toggleOpen} side={side} />
  );
};

export default ReferenceSidebar;
kirin-ri commented 6 months ago
      <div style={headerStyle}>
        <div style={{ display: 'flex', alignItems: 'center' }}>
          <IconFile style={{ marginRight: '8px' }} />
          参照リスト
        </div>
        <div onClick={toggleOpen} style={{ cursor: 'pointer' }}>
          {side === 'right' ? <IconArrowBarLeft /> : <IconArrowBarRight />}
        </div>
      </div>
kirin-ri commented 6 months ago
      <div style={{ display: 'flex', justifyContent: 'flex-end', padding: '10px' }}>
        {/* 关闭按钮,放在最上方,并向右对齐 */}
        <div onClick={toggleOpen} style={{ cursor: 'pointer' }}>
          {side === 'right' ? <IconArrowBarLeft /> : <IconArrowBarRight />}
        </div>
      </div>
      <div style={{ marginBottom: '20px', display: 'flex', alignItems: 'center', paddingLeft: '10px' }}>
        {/* 参照リスト标题 */}
        <IconFile style={{ marginRight: '8px' }} />
        参照リスト
      </div>
kirin-ri commented 6 months ago
    {/* 使用flex布局使标题和关闭按钮水平对齐,并在容器最右侧显示关闭按钮 */}
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%', padding: '0 10px' }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <IconFile style={{ marginRight: '8px' }} />
        参照リスト
      </div>
      {/* 将关闭按钮直接放在这里,而不是额外的div中 */}
      <div onClick={toggleOpen} style={{ cursor: 'pointer' }}>
        {side === 'right' ? <IconArrowBarLeft /> : <IconArrowBarRight />}
      </div>
    </div>
kirin-ri commented 6 months ago
import { IconFolderPlus, IconMistOff, IconPlus, IconFile } from '@tabler/icons-react';
import { ReactNode, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { IconArrowBarLeft, IconArrowBarRight } from '@tabler/icons-react';

import {
  CloseSidebarButton,
  OpenSidebarButton,
} from './components/OpenCloseButton';

interface Reference {
  id: string;
  title: string;
  description: string;
}

interface Props<T> {
  isOpen: boolean;
  side: 'left' | 'right';
  items: T[];
  toggleOpen: () => void;
}

const ReferenceSidebar = <T,>({
  isOpen,
  side,
  items,
  toggleOpen,
}: Props<T>) => {
  const { t } = useTranslation('promptbar');
  const [hoveredItemId, setHoveredItemId] = useState<string | null>(null);

  const allowDrop = (e: any) => {
    e.preventDefault();
  };

  const highlightDrop = (e: any) => {
    e.target.style.background = '#343541';
  };

  const removeHighlight = (e: any) => {
    e.target.style.background = 'none';
  };

  const itemsample = [
    {id:'1',title:'実績発電原子炉に係る新規制基準の考え方について.pdf',description:'…見直しを行うために「発電用軽水型原子炉の新規制基準に関する検討チーム」(以下「検討チーム」という。)を組織し、発電用軽水型原子炉の新規制基準策定のための検討を開始した。検討チーム…'},
    {id:'2',title:'実績発電原子炉に係る新規制基準の考え方について.pdf',description:'…見直しを行うために「発電用軽水型原子炉の新規制基準に関する検討チーム」(以下「検討チーム」という。)を組織し、発電用軽水型原子炉の新規制基準策定のための検討を開始した。検討チーム…'},
    {id:'3',title:'実績発電原子炉に係る新規制基準の考え方について.pdf',description:'…見直しを行うために「発電用軽水型原子炉の新規制基準に関する検討チーム」(以下「検討チーム」という。)を組織し、発電用軽水型原子炉の新規制基準策定のための検討を開始した。検討チーム…'},
    {id:'4',title:'実績発電原子炉に係る新規制基準の考え方について.pdf',description:'…見直しを行うために「発電用軽水型原子炉の新規制基準に関する検討チーム」(以下「検討チーム」という。)を組織し、発電用軽水型原子炉の新規制基準策定のための検討を開始した。検討チーム…'},
    {id:'5',title:'実績発電原子炉に係る新規制基準の考え方について.pdf',description:'…見直しを行うために「発電用軽水型原子炉の新規制基準に関する検討チーム」(以下「検討チーム」という。)を組織し、発電用軽水型原子炉の新規制基準策定のための検討を開始した。検討チーム…'},
  ]

  const headerStyle = {
    display: 'flex',
    justiftContent: 'space-between',
    alignItems:'center',
    fontSize: '15px',
    marginBottom: '15px',
    width: '100%',
  }
  const titleIcon = {
    display: 'flex',
    marginRight: '8px',
  }
  const listItemStyle = (isHovered: boolean) => ({
    marginBottom: '10px',
    border: '1px solid white',
    padding: '20px',
    borderRadius: '5px',
    backgroundColor: isHovered ? '#0056b3' : '#343541', // Change background color on hover
    transition: 'background-color 0.3s',
    maxWidth: '350px',
  });
  const listTitleStyle = {
    marginBottom: '10px',
  }
  return isOpen ? (
    <div>
      <div className={`fixed top-0 ${side}-0 z-40 flex h-full w-[400px] flex-none flex-col space-y-2 bg-[#202123] p-2 text-[14px] transition-all sm:relative sm:top-0`}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%', padding: '0 10px' }}>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <IconFile style={{ marginRight: '8px' }} />
            参照リスト
          </div>
          <div onClick={toggleOpen} style={{ cursor: 'pointer' }}>
            {side === 'right' ? <IconArrowBarRight /> : <IconArrowBarLeft />}
          </div>
        </div>
        <div  style={{overflowY:'auto',flex:1,padding:'0 10px'}}>
          <ul>
            {itemsample.map((item) => (
              <a href="test" target="_blank" rel="noopener noreferrer">
                <li
                style={listItemStyle(item.id === hoveredItemId)}
                onMouseEnter={() => setHoveredItemId(item.id)}
                onMouseLeave={() => setHoveredItemId(null)}
                >
                  <h3 style={listTitleStyle}>{item.title}</h3>
                  <p>{item.description}</p>
                </li>
              </a>
            ))}
          </ul>
        </div>
      </div>
    </div>
  ) : (
    <OpenSidebarButton onClick={toggleOpen} side={side} />
  );
};

export default ReferenceSidebar;
kirin-ri commented 6 months ago
      <Sidebar<Conversation>
        side={'left'}
        isOpen={showChatbar}
        addItemButtonTitle={t('New chat')}
        itemComponent={<Conversations conversations={filteredConversations} />}
        folderComponent={<ChatFolders searchTerm={searchTerm} />}
        items={filteredConversations}
        searchTerm={searchTerm}
        handleSearchTerm={(searchTerm: string) =>
          chatDispatch({ field: 'searchTerm', value: searchTerm })
        }
        toggleOpen={handleToggleChatbar}
        handleCreateItem={handleNewConversation}
        handleCreateFolder={() => handleCreateFolder(t('New folder'), 'chat')}
        handleDrop={handleDrop}
        footerComponent={<ChatbarSettings />}
      />
kirin-ri commented 6 months ago
      <Sidebar<Prompt>
        side={'right'}
        isOpen={showPromptbar}
        addItemButtonTitle={t('New prompt')}
        itemComponent={
          <Prompts
            prompts={filteredPrompts.filter((prompt) => !prompt.folderId)}
          />
        }
        folderComponent={<PromptFolders />}
        items={filteredPrompts}
        searchTerm={searchTerm}
        handleSearchTerm={(searchTerm: string) =>
          promptDispatch({ field: 'searchTerm', value: searchTerm })
        }
        toggleOpen={handleTogglePromptbar}
        handleCreateItem={handleCreatePrompt}
        handleCreateFolder={() => handleCreateFolder(t('New folder'), 'prompt')}
        handleDrop={handleDrop}
      />
kirin-ri commented 6 months ago
import { IconFolderPlus, IconMistOff, IconPlus } from '@tabler/icons-react';
import { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';

import {
  CloseSidebarButton,
  OpenSidebarButton,
} from './components/OpenCloseButton';

import Search from '../Search';
import Promptbar from '../Promptbar';

interface Props<T> {
  isOpen: boolean;
  addItemButtonTitle: string;
  side: 'left' | 'right';
  items: T[];
  itemComponent: ReactNode;
  folderComponent: ReactNode;
  footerComponent?: ReactNode;
  searchTerm: string;
  handleSearchTerm: (searchTerm: string) => void;
  toggleOpen: () => void;
  handleCreateItem: () => void;
  handleCreateFolder: () => void;
  handleDrop: (e: any) => void;
}

const Sidebar = <T,>({
  isOpen,
  addItemButtonTitle,
  side,
  items,
  itemComponent,
  folderComponent,
  footerComponent,
  searchTerm,
  handleSearchTerm,
  toggleOpen,
  handleCreateItem,
  handleCreateFolder,
  handleDrop,
}: Props<T>) => {
  const { t } = useTranslation('promptbar');

  const allowDrop = (e: any) => {
    e.preventDefault();
  };

  const highlightDrop = (e: any) => {
    e.target.style.background = '#343541';
  };

  const removeHighlight = (e: any) => {
    e.target.style.background = 'none';
  };

  return isOpen ? (
    <div>
      <div
        className={`fixed top-0 ${side}-0 z-40 flex h-full w-[260px] flex-none flex-col space-y-2 bg-[#202123] p-2 text-[14px] transition-all sm:relative sm:top-0`}
      >
        <div className="flex items-center">
          <button
            className="text-sidebar flex w-[190px] flex-shrink-0 cursor-pointer select-none items-center gap-3 rounded-md border border-white/20 p-3 text-white transition-colors duration-200 hover:bg-gray-500/10"
            onClick={() => {
              handleCreateItem();
              handleSearchTerm('');
            }}
          >
            <IconPlus size={16} />
            {addItemButtonTitle}
          </button>

          <button
            className="ml-2 flex flex-shrink-0 cursor-pointer items-center gap-3 rounded-md border border-white/20 p-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
            onClick={handleCreateFolder}
          >
            <IconFolderPlus size={16} />
          </button>
        </div>
        <Search
          placeholder={t('Search...') || ''}
          searchTerm={searchTerm}
          onSearch={handleSearchTerm}
        />

        <div className="flex-grow overflow-auto">
          {items?.length > 0 && (
            <div className="flex border-b border-white/20 pb-2">
              {folderComponent}
            </div>
          )}

          {items?.length > 0 ? (
            <div
              className="pt-2"
              onDrop={handleDrop}
              onDragOver={allowDrop}
              onDragEnter={highlightDrop}
              onDragLeave={removeHighlight}
            >
              {itemComponent}
            </div>
          ) : (
            <div className="mt-8 select-none text-center text-white opacity-50">
              <IconMistOff className="mx-auto mb-3" />
              <span className="text-[14px] leading-normal">
                {t('No data.')}
              </span>
            </div>
          )}
        </div>
        {footerComponent}
      </div>

      <CloseSidebarButton onClick={toggleOpen} side={side} />
    </div>
  ) : (
    <OpenSidebarButton onClick={toggleOpen} side={side} />
  );
};

export default Sidebar;
kirin-ri commented 6 months ago
<Sidebar
  isOpen={showSidebar}
  toggleOpen={handleToggleSidebar}
  side={'left'}
>
  {showChatbar && (
    <>
      <div>
        <h2>{t('Chat')}</h2>
        <Conversations conversations={filteredConversations} />
        <ChatFolders searchTerm={searchTerm} />
        <button onClick={handleNewConversation}>{t('New chat')}</button>
      </div>
      <div>
        <h2>{t('Prompt')}</h2>
        <Prompts prompts={filteredPrompts.filter((prompt) => !prompt.folderId)} />
        <PromptFolders />
        <button onClick={handleCreatePrompt}>{t('New prompt')}</button>
      </div>
    </>
  )}
</Sidebar>
kirin-ri commented 6 months ago
<Sidebar
  isOpen={showSidebar}
  toggleOpen={handleToggleSidebar}
  side={'left'}
  addItemButtonTitle={showChatbar ? t('New chat') : t('New prompt')}
  searchTerm={searchTerm}
  handleSearchTerm={(searchTerm: string) =>
    showChatbar
      ? chatDispatch({ field: 'searchTerm', value: searchTerm })
      : promptDispatch({ field: 'searchTerm', value: searchTerm })
  }
  handleCreateItem={showChatbar ? handleNewConversation : handleCreatePrompt}
  handleCreateFolder={() =>
    showChatbar
      ? handleCreateFolder(t('New folder'), 'chat')
      : handleCreateFolder(t('New folder'), 'prompt')
  }
  handleDrop={handleDrop}
>
  <div className="flex flex-col h-full">
    {showChatbar ? (
      <>
        <div>
          <h2>{t('Chat')}</h2>
          <Conversations conversations={filteredConversations} />
          <ChatFolders searchTerm={searchTerm} />
        </div>
        <ChatbarSettings />
      </>
    ) : (
      <div>
        <h2>{t('Prompt')}</h2>
        <Prompts
          prompts={filteredPrompts.filter((prompt) => !prompt.folderId)}
        />
        <PromptFolders />
      </div>
    )}
  </div>
</Sidebar>
kirin-ri commented 6 months ago
import { useCallback, useContext, useEffect } from 'react';

import { useTranslation } from 'next-i18next';

import { useCreateReducer } from '@/hooks/useCreateReducer';

import { DEFAULT_SYSTEM_PROMPT, DEFAULT_TEMPERATURE } from '@/utils/app/const';
import { saveConversation, saveConversations } from '@/utils/app/conversation';
import { saveFolders } from '@/utils/app/folders';
import { exportData, importData } from '@/utils/app/importExport';

import { Conversation } from '@/types/chat';
import { LatestExportFormat, SupportedExportFormats } from '@/types/export';
import { OpenAIModels } from '@/types/openai';
import { PluginKey } from '@/types/plugin';

import HomeContext from '@/pages/api/home/home.context';

import { ChatFolders } from './components/ChatFolders';
import { ChatbarSettings } from './components/ChatbarSettings';
import { Conversations } from './components/Conversations';

import Sidebar from '../Sidebar';
import ChatbarContext from './Chatbar.context';
import { ChatbarInitialState, initialState } from './Chatbar.state';

import { v4 as uuidv4 } from 'uuid';

export const Chatbar = () => {
  const { t } = useTranslation('sidebar');

  const chatBarContextValue = useCreateReducer<ChatbarInitialState>({
    initialState,
  });

  const {
    state: { conversations, showChatbar, defaultModelId, folders, pluginKeys },
    dispatch: homeDispatch,
    handleCreateFolder,
    handleNewConversation,
    handleUpdateConversation,
  } = useContext(HomeContext);

  const {
    state: { searchTerm, filteredConversations },
    dispatch: chatDispatch,
  } = chatBarContextValue;

  const handleApiKeyChange = useCallback(
    (apiKey: string) => {
      homeDispatch({ field: 'apiKey', value: apiKey });

      localStorage.setItem('apiKey', apiKey);
    },
    [homeDispatch],
  );

  const handlePluginKeyChange = (pluginKey: PluginKey) => {
    if (pluginKeys.some((key) => key.pluginId === pluginKey.pluginId)) {
      const updatedPluginKeys = pluginKeys.map((key) => {
        if (key.pluginId === pluginKey.pluginId) {
          return pluginKey;
        }

        return key;
      });

      homeDispatch({ field: 'pluginKeys', value: updatedPluginKeys });

      localStorage.setItem('pluginKeys', JSON.stringify(updatedPluginKeys));
    } else {
      homeDispatch({ field: 'pluginKeys', value: [...pluginKeys, pluginKey] });

      localStorage.setItem(
        'pluginKeys',
        JSON.stringify([...pluginKeys, pluginKey]),
      );
    }
  };

  const handleClearPluginKey = (pluginKey: PluginKey) => {
    const updatedPluginKeys = pluginKeys.filter(
      (key) => key.pluginId !== pluginKey.pluginId,
    );

    if (updatedPluginKeys.length === 0) {
      homeDispatch({ field: 'pluginKeys', value: [] });
      localStorage.removeItem('pluginKeys');
      return;
    }

    homeDispatch({ field: 'pluginKeys', value: updatedPluginKeys });

    localStorage.setItem('pluginKeys', JSON.stringify(updatedPluginKeys));
  };

  const handleExportData = () => {
    exportData();
  };

  const handleImportConversations = (data: SupportedExportFormats) => {
    const { history, folders, prompts }: LatestExportFormat = importData(data);
    homeDispatch({ field: 'conversations', value: history });
    homeDispatch({
      field: 'selectedConversation',
      value: history[history.length - 1],
    });
    homeDispatch({ field: 'folders', value: folders });
    homeDispatch({ field: 'prompts', value: prompts });

    window.location.reload();
  };

  const handleClearConversations = () => {
    defaultModelId &&
      homeDispatch({
        field: 'selectedConversation',
        value: {
          id: uuidv4(),
          name: t('New Conversation'),
          messages: [],
          model: OpenAIModels[defaultModelId],
          prompt: DEFAULT_SYSTEM_PROMPT,
          temperature: DEFAULT_TEMPERATURE,
          folderId: null,
        },
      });

    homeDispatch({ field: 'conversations', value: [] });

    localStorage.removeItem('conversationHistory');
    localStorage.removeItem('selectedConversation');

    const updatedFolders = folders.filter((f) => f.type !== 'chat');

    homeDispatch({ field: 'folders', value: updatedFolders });
    saveFolders(updatedFolders);
  };

  const handleDeleteConversation = (conversation: Conversation) => {
    const updatedConversations = conversations.filter(
      (c) => c.id !== conversation.id,
    );

    homeDispatch({ field: 'conversations', value: updatedConversations });
    chatDispatch({ field: 'searchTerm', value: '' });
    saveConversations(updatedConversations);

    if (updatedConversations.length > 0) {
      homeDispatch({
        field: 'selectedConversation',
        value: updatedConversations[updatedConversations.length - 1],
      });

      saveConversation(updatedConversations[updatedConversations.length - 1]);
    } else {
      defaultModelId &&
        homeDispatch({
          field: 'selectedConversation',
          value: {
            id: uuidv4(),
            name: t('New Conversation'),
            messages: [],
            model: OpenAIModels[defaultModelId],
            prompt: DEFAULT_SYSTEM_PROMPT,
            temperature: DEFAULT_TEMPERATURE,
            folderId: null,
          },
        });

      localStorage.removeItem('selectedConversation');
    }
  };

  const handleToggleChatbar = () => {
    homeDispatch({ field: 'showChatbar', value: !showChatbar });
    localStorage.setItem('showChatbar', JSON.stringify(!showChatbar));
  };

  const handleDrop = (e: any) => {
    if (e.dataTransfer) {
      const conversation = JSON.parse(e.dataTransfer.getData('conversation'));
      handleUpdateConversation(conversation, { key: 'folderId', value: 0 });
      chatDispatch({ field: 'searchTerm', value: '' });
      e.target.style.background = 'none';
    }
  };

  useEffect(() => {
    if (searchTerm) {
      chatDispatch({
        field: 'filteredConversations',
        value: conversations.filter((conversation) => {
          const searchable =
            conversation.name.toLocaleLowerCase() +
            ' ' +
            conversation.messages.map((message) => message.content).join(' ');
          return searchable.toLowerCase().includes(searchTerm.toLowerCase());
        }),
      });
    } else {
      chatDispatch({
        field: 'filteredConversations',
        value: conversations,
      });
    }
  }, [searchTerm, conversations]);

  return (
    <ChatbarContext.Provider
      value={{
        ...chatBarContextValue,
        handleDeleteConversation,
        handleClearConversations,
        handleImportConversations,
        handleExportData,
        handlePluginKeyChange,
        handleClearPluginKey,
        handleApiKeyChange,
      }}
    >
      <Sidebar<Conversation>
        side={'left'}
        isOpen={showChatbar}
        addItemButtonTitle={t('New chat')}
        itemComponent={<Conversations conversations={filteredConversations} />}
        folderComponent={<ChatFolders searchTerm={searchTerm} />}
        items={filteredConversations}
        searchTerm={searchTerm}
        handleSearchTerm={(searchTerm: string) =>
          chatDispatch({ field: 'searchTerm', value: searchTerm })
        }
        toggleOpen={handleToggleChatbar}
        handleCreateItem={handleNewConversation}
        handleCreateFolder={() => handleCreateFolder(t('New folder'), 'chat')}
        handleDrop={handleDrop}
        footerComponent={<ChatbarSettings />}
      />
    </ChatbarContext.Provider>
  );
};
``