From b6871accaceff0728bb62d8e5a0bc09ce7c43328 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Mon, 22 May 2023 02:11:19 -0700 Subject: [PATCH] Settings: Migrate to Tabs --- src/apps/settings/SettingsModal.tsx | 37 ++- src/common/state/store-ui.ts | 10 +- src/modules/elevenlabs/ElevenlabsSettings.tsx | 119 ++++---- src/modules/google/SearchSettings.tsx | 87 +++--- src/modules/prodia/ProdiaSettings.tsx | 255 +++++++++--------- 5 files changed, 252 insertions(+), 256 deletions(-) diff --git a/src/apps/settings/SettingsModal.tsx b/src/apps/settings/SettingsModal.tsx index 2e985172b..673b72217 100644 --- a/src/apps/settings/SettingsModal.tsx +++ b/src/apps/settings/SettingsModal.tsx @@ -1,26 +1,28 @@ import * as React from 'react'; -import { Box, Button, Divider } from '@mui/joy'; +import { Button, Divider, Tab, TabList, TabPanel, Tabs } from '@mui/joy'; import BuildCircleIcon from '@mui/icons-material/BuildCircle'; import { ElevenlabsSettings } from '~/modules/elevenlabs/ElevenlabsSettings'; -import { GoodModal } from '~/common/components/GoodModal'; import { ProdiaSettings } from '~/modules/prodia/ProdiaSettings'; import { SearchSettings } from '~/modules/google/SearchSettings'; + +import { GoodModal } from '~/common/components/GoodModal'; import { useUIStateStore } from '~/common/state/store-ui'; import { UISettings } from './UISettings'; + /** * Component that allows the User to modify the application settings, * persisted on the client via localStorage. */ export function SettingsModal() { // external state - const { settingsOpen, closeSettings, openModelsSetup } = useUIStateStore(); + const { settingsOpenTab, closeSettings, openModelsSetup } = useUIStateStore(); return ( - }> Models @@ -30,17 +32,30 @@ export function SettingsModal() { - + + + UI + Draw + Speak + Search + - + + + - + + + - + + + - - - + + + + diff --git a/src/common/state/store-ui.ts b/src/common/state/store-ui.ts index 1c3a1032b..6f4973a66 100644 --- a/src/common/state/store-ui.ts +++ b/src/common/state/store-ui.ts @@ -8,8 +8,8 @@ import { DLLMId } from '~/modules/llms/llm.types'; interface UIStateStore { - settingsOpen: boolean; - openSettings: () => void; + settingsOpenTab: number; // 0: closed, 1..N: tab index + openSettings: (tab?: number) => void; closeSettings: () => void; modelsSetupOpen: boolean; @@ -25,9 +25,9 @@ interface UIStateStore { export const useUIStateStore = create()( (set) => ({ - settingsOpen: false, - closeSettings: () => set({ settingsOpen: false }), - openSettings: () => set({ settingsOpen: true }), + settingsOpenTab: 0, + openSettings: (tab?: number) => set({ settingsOpenTab: tab || 1 }), + closeSettings: () => set({ settingsOpenTab: 0 }), modelsSetupOpen: false, openModelsSetup: () => set({ modelsSetupOpen: true }), diff --git a/src/modules/elevenlabs/ElevenlabsSettings.tsx b/src/modules/elevenlabs/ElevenlabsSettings.tsx index 307eb13f6..a62634932 100644 --- a/src/modules/elevenlabs/ElevenlabsSettings.tsx +++ b/src/modules/elevenlabs/ElevenlabsSettings.tsx @@ -1,16 +1,13 @@ import * as React from 'react'; import { shallow } from 'zustand/shallow'; -import { Box, CircularProgress, FormControl, FormHelperText, FormLabel, IconButton, Input, Option, Radio, RadioGroup, Select, Stack } from '@mui/joy'; -import KeyIcon from '@mui/icons-material/Key'; +import { Box, CircularProgress, FormControl, FormHelperText, FormLabel, Option, Radio, RadioGroup, Select, Stack } from '@mui/joy'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import RecordVoiceOverIcon from '@mui/icons-material/RecordVoiceOver'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { apiQuery } from '~/modules/trpc/trpc.client'; -import { Section } from '~/common/components/Section'; +import { FormInputKey } from '~/common/components/FormInputKey'; import { settingsCol1Width, settingsGap } from '~/common/theme'; import { isElevenLabsEnabled, requireUserKeyElevenLabs } from './elevenlabs.client'; @@ -18,9 +15,6 @@ import { useElevenlabsStore } from './store-elevenlabs'; export function ElevenlabsSettings() { - // state - const [showApiKeyValue, setShowApiKeyValue] = React.useState(false); - // external state const { apiKey, setApiKey, voiceId, setVoiceId, autoSpeak, setAutoSpeak } = useElevenlabsStore(state => ({ apiKey: state.elevenLabsApiKey, setApiKey: state.setElevenLabsApiKey, @@ -36,76 +30,61 @@ export function ElevenlabsSettings() { staleTime: 1000 * 60 * 5, // 5 minutes }); - const handleToggleApiKeyVisibility = () => setShowApiKeyValue(!showApiKeyValue); - - const handleApiKeyChange = (e: React.ChangeEvent) => setApiKey(e.target.value); - const handleVoiceChange = (e: any, value: string | null) => setVoiceId(value || ''); const handleAutoSpeakChange = (e: React.ChangeEvent) => setAutoSpeak((e.target.value || 'off') as 'off' | 'firstLine'); return ( -
- + - - - - ElevenLabs API Key - - - {requiresKey ? '(required)' : '(optional)'} - - - } - endDecorator={!!apiKey && ( - - {showApiKeyValue ? : } - - )} - slotProps={{ input: { sx: { width: '100%' } } }} - sx={{ width: '100%' }} - /> - + + 📢 Hear AI responses, even in your own voice + - - - Assistant voice - - - + updateSetup({ oaiKey: value })} + // required={!hasServerKeyOpenAI} isError={keyError} + placeholder='sk-...' + /> - - - Speak responses - {autoSpeak === 'off' ? 'Off' : 'Just the first line'} - - - - - - + + + Assistant Voice + + + - -
+ + + Speak Responses + {autoSpeak === 'off' ? 'Off' : 'First paragraph'} + + + + + + + + + ); } \ No newline at end of file diff --git a/src/modules/google/SearchSettings.tsx b/src/modules/google/SearchSettings.tsx index 09031bc0d..8e6ea2020 100644 --- a/src/modules/google/SearchSettings.tsx +++ b/src/modules/google/SearchSettings.tsx @@ -7,7 +7,6 @@ import SearchIcon from '@mui/icons-material/Search'; import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; import { Link } from '~/common/components/Link'; -import { Section } from '~/common/components/Section'; import { settingsCol1Width, settingsGap } from '~/common/theme'; import { isValidGoogleCloudApiKey, isValidGoogleCseId, requireUserKeyGoogleCse } from './search.client'; @@ -30,50 +29,52 @@ export function SearchSettings() { const handleCseIdChange = (e: React.ChangeEvent) => setGoogleCSEId(e.target.value); return ( -
- + - - - - - Google Cloud API Key - - - - Create one here - - - } - slotProps={{ input: { sx: { width: '100%' } } }} - sx={{ width: '100%' }} - /> - + + 🔍 Helps in locating up-to-date websites. Bring your own Google Keys. + - - - - - Google CSE ID - - - - Get it here - - - } - slotProps={{ input: { sx: { width: '100%' } } }} - sx={{ width: '100%' }} - /> - + + + + + Google Cloud API Key + + + + Create one here + + + } + slotProps={{ input: { sx: { width: '100%' } } }} + sx={{ width: '100%' }} + /> + - -
+ + + + + Google CSE ID + + + + Get it here + + + } + slotProps={{ input: { sx: { width: '100%' } } }} + sx={{ width: '100%' }} + /> + + + ); } \ No newline at end of file diff --git a/src/modules/prodia/ProdiaSettings.tsx b/src/modules/prodia/ProdiaSettings.tsx index 89dd70e12..1c79200ff 100644 --- a/src/modules/prodia/ProdiaSettings.tsx +++ b/src/modules/prodia/ProdiaSettings.tsx @@ -10,8 +10,6 @@ import VisibilityIcon from '@mui/icons-material/Visibility'; import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { apiQuery } from '~/modules/trpc/trpc.client'; - -import { Section } from '~/common/components/Section'; import { settingsGap } from '~/common/theme'; import { isValidProdiaApiKey, prodiaDefaultModelId, requireUserKeyProdia } from './prodia.client'; @@ -50,139 +48,142 @@ export function ProdiaSettings() { const colWidth = 150; return ( -
- + - - - - Prodia API Key - - - {requiresKey ? '(required)' : '(optional)'} - - - } - endDecorator={!!apiKey && ( - - {showApiKeyValue ? : } - - )} - slotProps={{ input: { sx: { width: '100%' } } }} - sx={{ width: '100%' }} - /> - + + 🎨 Turn text into pictures and /imagine. Bring your own Prodia.com API Key. + - + + - Diffusion Model + Prodia API Key - - + + {requiresKey ? '(required)' : '(optional)'} + + - - - - - Negative Prompt - - - - {negativePrompt ? 'Custom' : 'Not set'} - - - setNegativePrompt(e.target.value)} - slotProps={{ input: { sx: { width: '100%' } } }} - sx={{ width: '100%' }} - /> - + } + endDecorator={!!apiKey && ( + + {showApiKeyValue ? : } + + )} + slotProps={{ input: { sx: { width: '100%' } } }} + sx={{ width: '100%' }} + /> + - - - - - Diffusion Steps - - - - {steps === 25 ? 'Default' : steps > 30 ? (steps > 40 ? 'May be unnecessary' : 'More detail') : steps <= 15 ? 'Less detail' : 'Balanced'} - - - setSteps(value as number)} - min={10} max={50} step={1} defaultValue={25} - sx={{ width: '100%' }} - /> - + + + Diffusion Model + + + - - - - - Cfg-Scale - - - - {cfgScale === 7 ? 'Default' : cfgScale >= 9 ? (cfgScale >= 12 ? 'Heavy guidance' : 'Intense guidance') : cfgScale <= 5 ? 'More freedom' : 'Balanced'} - - - setCfgScale(value as number)} - min={1} max={15} step={0.5} defaultValue={7} - sx={{ width: '100%' }} - /> - + + + + + Negative Prompt + + + + {negativePrompt ? 'Custom' : 'Not set'} + + + setNegativePrompt(e.target.value)} + slotProps={{ input: { sx: { width: '100%' } } }} + sx={{ width: '100%' }} + /> + - - - - - Noise Seed - - - - {seed ? 'Custom' : 'Random'} - - - setSeed(e.target.value || '')} - slotProps={{ - input: { - type: 'number', - sx: { width: '100%' }, - }, - }} - sx={{ width: '100%' }} - /> - + + + + + Diffusion Steps + + + + {steps === 25 ? 'Default' : steps > 30 ? (steps > 40 ? 'May be unnecessary' : 'More detail') : steps <= 15 ? 'Less detail' : 'Balanced'} + + + setSteps(value as number)} + min={10} max={50} step={1} defaultValue={25} + sx={{ width: '100%' }} + /> + - -
+ + + + + Cfg-Scale + + + + {cfgScale === 7 ? 'Default' : cfgScale >= 9 ? (cfgScale >= 12 ? 'Heavy guidance' : 'Intense guidance') : cfgScale <= 5 ? 'More freedom' : 'Balanced'} + + + setCfgScale(value as number)} + min={1} max={15} step={0.5} defaultValue={7} + sx={{ width: '100%' }} + /> + + + + + + + Noise Seed + + + + {seed ? 'Custom' : 'Random'} + + + setSeed(e.target.value || '')} + slotProps={{ + input: { + type: 'number', + sx: { width: '100%' }, + }, + }} + sx={{ width: '100%' }} + /> + + + ); } \ No newline at end of file