Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
import { Footer } from './components/Footer';
import Header from './components/Header';
import { ModalProvider } from './components/ModalProvider';
import SettingDialog from './components/SettingDialog';
import Sidebar from './components/Sidebar';
import { ToastPopup } from './components/ToastPopup';
import { AppContextProvider, useAppContext } from './context/app';
Expand All @@ -24,6 +23,7 @@ import { useDebouncedCallback } from './hooks/useDebouncedCallback';
import { usePWAUpdatePrompt } from './hooks/usePWAUpdatePrompt';
import * as lang from './lang/en.json';
import ChatScreen from './pages/ChatScreen';
import Settings from './pages/Settings';
import WelcomeScreen from './pages/WelcomeScreen';

const DEBOUNCE_DELAY = 5000;
Expand All @@ -43,6 +43,7 @@ const App: FC = () => {
<Routes>
<Route element={<AppLayout />}>
<Route path="/chat/:convId" element={<Chat />} />
<Route path="/settings" element={<Settings />} />
<Route path="*" element={<WelcomeScreen />} />
</Route>
</Routes>
Expand Down Expand Up @@ -141,12 +142,6 @@ const AppLayout: FC = () => {
</main>
<Footer />
</div>
{showSettings && (
<SettingDialog
show={showSettings}
onClose={() => setShowSettings(false)}
/>
)}
<Toaster />
</>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from '@heroicons/react/24/solid';
import { useEffect } from 'react';
import toast from 'react-hot-toast';
import { useAppContext } from '../context/app';
import { useNavigate } from 'react-router';
import { useChatExtraContext } from '../hooks/useChatExtraContext';
import { ChatTextareaApi, useChatTextarea } from '../hooks/useChatTextarea';
import { useVSCodeContext } from '../hooks/useVSCode';
Expand Down Expand Up @@ -42,7 +42,7 @@ export function ChatInput({
onStop: () => void;
isGenerating: boolean;
}) {
const { setShowSettings } = useAppContext();
const navigate = useNavigate();
const textarea: ChatTextareaApi = useChatTextarea(getPrefilledContent());
const extraContext = useChatExtraContext();
useVSCodeContext(textarea, extraContext);
Expand Down Expand Up @@ -135,7 +135,7 @@ export function ChatInput({
className="btn btn-ghost w-8 h-8 p-0 rounded-full xl:hidden"
title="Settings"
aria-label="Open settings menu"
onClick={() => setShowSettings(true)}
onClick={() => navigate('/settings')}
>
<AdjustmentsHorizontalIcon className="h-5 w-5" />
</button>
Expand Down
115 changes: 67 additions & 48 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@ export default function Header() {
const {
config,
config: { model },
setShowSettings,
saveConfig,
showSettings,
} = useAppContext();
const { models } = useInferenceContext();
const { viewingChat } = useChatContext();

const currConv = useMemo(() => viewingChat?.conv ?? null, [viewingChat]);
const title = useMemo(
() => (currConv ? currConv.name : lang['header.title.noChat']),
[currConv]
() =>
showSettings
? lang['header.title.settings']
: currConv
? currConv.name
: lang['header.title.noChat'],
[currConv, showSettings]
);

const selectedModel = useMemo(() => {
Expand All @@ -47,6 +52,7 @@ export default function Header() {
aria-label={title}
role="button"
onClick={() => {
if (showSettings) return;
if (currConv) navigate(`/chat/${currConv.id}`);
else navigate('/');
}}
Expand All @@ -64,53 +70,66 @@ export default function Header() {
</button>
</section>

<section className="flex flex-row items-center">
{/* model information */}
<Dropdown
className="ml-2 px-1 xl:px-4 py-0"
entity="Model"
options={models.map((model) => ({
value: model.id,
label: model.name,
}))}
filterable={true}
hideChevron={models.length < 2}
align="start"
currentValue={
<span className="max-w-64 sm:max-w-80 truncate text-nowrap font-semibold">
{selectedModel}
</span>
}
renderOption={(option) => (
<span className="max-w-64 sm:max-w-80 truncate text-nowrap">
{option.label}
</span>
)}
isSelected={(option) => model === option.value}
onSelect={(option) =>
saveConfig({
...config,
model: option.value,
})
}
/>
{showSettings && (
<section className="flex items-center max-xl:hidden">
<label
className="font-medium truncate text-center px-4"
aria-label={title}
>
{title}
</label>
</section>
)}

{/* spacer */}
<div className="grow"></div>
{!showSettings && (
<section className="flex flex-row items-center">
{/* model information */}
<Dropdown
className="ml-2 px-1 xl:px-4 py-0"
entity="Model"
options={models.map((model) => ({
value: model.id,
label: model.name,
}))}
filterable={true}
hideChevron={models.length < 2}
align="start"
currentValue={
<span className="max-w-64 sm:max-w-80 truncate text-nowrap font-semibold">
{selectedModel}
</span>
}
renderOption={(option) => (
<span className="max-w-64 sm:max-w-80 truncate text-nowrap">
{option.label}
</span>
)}
isSelected={(option) => model === option.value}
onSelect={(option) =>
saveConfig({
...config,
model: option.value,
})
}
/>

{/* action buttons (top right) */}
<div className="flex items-center">
<button
className="btn btn-ghost w-8 h-8 p-0 rounded-full max-xl:hidden"
title="Settings"
aria-label="Open settings menu"
onClick={() => setShowSettings(true)}
>
{/* settings button */}
<Cog8ToothIcon className="w-5 h-5" />
</button>
</div>
</section>
{/* spacer */}
<div className="grow"></div>

{/* action buttons (top right) */}
<div className="flex items-center">
<button
className="btn btn-ghost w-8 h-8 p-0 rounded-full max-xl:hidden"
title="Settings"
aria-label="Open settings menu"
onClick={() => navigate('/settings')}
>
{/* settings button */}
<Cog8ToothIcon className="w-5 h-5" />
</button>
</div>
</section>
)}
</header>
);
}
14 changes: 10 additions & 4 deletions src/lang/en.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"header.title.noChat": "New Chat",
"header.title.settings": "Settings",
"chatScreen": {
"welcome": "Hi,",
"welcomeNote": "how can I help you?"
Expand Down Expand Up @@ -143,12 +144,12 @@
"note": "DRY sampling reduces repetition in generated text even across long contexts. This parameter sets DRY penalty for the last n tokens."
},
"custom": {
"label": "",
"note": ""
"label": "Custom JSON config",
"note": "For more info, refer to <a class=\"underline\" href=\"https://github.com/ggerganov/llama.cpp/blob/master/tools/server/README.md\" target=\"_blank\" rel=\"noopener noreferrer\">server documentation</a>"
},
"pyIntepreterEnabled": {
"label": "",
"note": ""
"label": "Enable Python interpreter",
"note": "This feature uses <a class=\"underline\" href=\"https://pyodide.org\" target=\"_blank\" rel=\"noopener noreferrer\">pyodide</a>, downloaded from CDN. To use this feature, ask the LLM to generate Python code inside a Markdown code block. You will see a \"Run\" button on the code block, near the \"Copy\" button."
},
"ttsPitch": {
"label": "Pitch",
Expand Down Expand Up @@ -192,6 +193,11 @@
"label": "Syntax Theme",
"note": "Choose the color theme for code blocks."
}
},
"actionButtons": {
"saveBtnLabel": "Save",
"cancelBtnLabel": "Close",
"resetBtnLabel": "Reset"
}
},
"newVersion": {
Expand Down
Loading