Skip to content

Commit

Permalink
Refactor: update settings ui with react router
Browse files Browse the repository at this point in the history
  • Loading branch information
mrdjohnson committed Nov 14, 2024
1 parent 6182596 commit 2b01d51
Show file tree
Hide file tree
Showing 29 changed files with 1,045 additions and 722 deletions.
53 changes: 29 additions & 24 deletions src/components/BreadcrumbBar.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
import { useNavigate } from 'react-router-dom'
import { observer } from 'mobx-react-lite'
import { Breadcrumbs, BreadcrumbItem } from '@nextui-org/react'
import _ from 'lodash'

export type BreadcrumbType = {
isSelected: boolean
label: string
onClick: () => void
path: string
}

const BreadcrumbBar = observer(
({ breadcrumbs }: { breadcrumbs: Array<BreadcrumbType | undefined | false> }) => {
return (
<Breadcrumbs className="mb-2">
{_.compact(breadcrumbs).map(breadcrumb => (
<BreadcrumbItem
className={
breadcrumb.isSelected
? ' [&>*]:!text-primary'
: 'scale-90 [&>*]:!text-base-content/70'
}
isCurrent={breadcrumb.isSelected}
onPress={breadcrumb.onClick}
key={breadcrumb.label}
>
{breadcrumb.label}
</BreadcrumbItem>
))}
</Breadcrumbs>
)
},
)
type BreadcrumbBarProps = {
breadcrumbs: BreadcrumbType[]
}

// Gets a list of crumbs and paths and determines the selected one (might be overkill and maybe I should just use the last index)
const BreadcrumbBar = observer(({ breadcrumbs }: BreadcrumbBarProps) => {
const navigate = useNavigate()

return (
<Breadcrumbs className="mt-2 place-self-center rounded-lg bg-base-content/10 p-2">
{breadcrumbs.map((breadcrumb, index) => (
<BreadcrumbItem
className={
index === breadcrumbs.length - 1
? ' [&>*]:!text-primary'
: 'underline decoration-base-content/70 [&>*]:!text-base-content/70'
}
isCurrent={index === breadcrumbs.length - 1}
key={breadcrumb.label}
onClick={() => navigate(breadcrumb.path)}
>
{breadcrumb.label}
</BreadcrumbItem>
))}
</Breadcrumbs>
)
})

export default BreadcrumbBar
9 changes: 4 additions & 5 deletions src/components/ChatBoxInputRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { observer } from 'mobx-react-lite'
import TextareaAutosize from 'react-textarea-autosize'

import { ChatViewModel } from '~/core/chat/ChatViewModel'
import { settingStore } from '~/core/setting/SettingStore'
import { personaStore } from '~/core/persona/PersonaStore'
import { connectionStore } from '~/core/connection/ConnectionStore'
import { incomingMessageStore } from '~/core/IncomingMessageStore'

import AttachmentWrapper from '~/components/AttachmentWrapper'
import CachedImage from '~/components/CachedImage'
import { NavButton } from '~/components/NavButton'

import { TransferHandler } from '~/utils/transfer/TransferHandler'

Expand Down Expand Up @@ -198,16 +198,15 @@ const ChatBoxInputRow = observer(({ chat, onSend, children }: ChatBoxInputRowPro
onSubmit={onFormSubmit}
>
<div className="join-item flex w-full flex-col justify-between bg-base-200 align-middle md:flex-row md:gap-2">
<button
<NavButton
tabIndex={0}
type="button"
to="/personas"
className="btn btn-active hidden rounded-none rounded-bl-md md:flex"
disabled={inputDisabled || connectionStore.isImageGenerationMode}
onClick={() => settingStore.openSettingsModal('personas')}
>
{personaStore.selectedPersona?.name || 'No personas selected'}
<ChevronDown />
</button>
</NavButton>

<div className="flex">
<AttachmentWrapper className="mr-auto md:mr-0">
Expand Down
21 changes: 11 additions & 10 deletions src/components/ChatBoxPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { observer } from 'mobx-react-lite'

import AttachmentWrapper from '~/components/AttachmentWrapper'
import FunTitle from '~/components/FunTitle'
import { NavButtonDiv } from '~/components/NavButton'

import { ChatViewModel } from '~/core/chat/ChatViewModel'
import { personaTable } from '~/core/persona/PersonaTable'
import { connectionStore } from '~/core/connection/ConnectionStore'
import { settingStore } from '~/core/setting/SettingStore'

type StepProps = { isCompleted?: boolean; type?: 'primary' | 'secondary'; inCompleteIcon?: string }

Expand Down Expand Up @@ -71,16 +72,16 @@ const ChatBoxPrompt = observer(({ chat }: { chat: ChatViewModel }) => {
</h1>

<div className="text-2xl">
<ul className="steps steps-vertical list-inside list-disc gap-3 py-6 text-left *:text-lg [&_a]:text-lg [&_span]:text-lg">
<ul className="steps steps-vertical list-inside list-disc gap-3 py-6 text-left *:!text-lg [&_a]:text-lg [&_span]:text-lg">
<Step isCompleted={!_.isEmpty(activeConnectionTypes)}>
{'Tell LM Studio, Ollama, AUTOMATIC1111, or Open AI that '}
<span className="text-primary">we're cool:</span>
<button
className="link decoration-primary"
onClick={() => settingStore.openSettingsModal('connection')}
<NavButtonDiv
to="/connection"
className="link inline-block text-lg decoration-primary"
>
How to connect
</button>
</NavButtonDiv>
</Step>

<Step isCompleted={anyConnectionHasModels}>
Expand All @@ -107,12 +108,12 @@ const ChatBoxPrompt = observer(({ chat }: { chat: ChatViewModel }) => {
<Step type="secondary" isCompleted={hasCreatedPersonas}>
{'Create and Select a'}

<button
className="link ml-1 decoration-secondary"
onClick={() => settingStore.openSettingsModal('personas')}
<NavButtonDiv
to="/personas"
className="link ml-1 inline-block decoration-secondary"
>
Persona <span className="text-xs">(aka System Prompt)</span>
</button>
</NavButtonDiv>

{'to give your bot some pizzaz'}
</Step>
Expand Down
22 changes: 9 additions & 13 deletions src/components/HostInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import Question from '~/icons/Question'
import Refresh from '~/icons/Refresh'

import FormInput from '~/components/form/FormInput'
import { settingStore } from '~/core/setting/SettingStore'
import { NavButtonDiv } from '~/components/NavButton'

import { ConnectionViewModelTypes } from '~/core/connection/viewModels'
import { ConnectionModel } from '~/core/connection/ConnectionModel'

Expand All @@ -27,13 +28,9 @@ const HostInput = observer(({ connection, isEnabled }: HostInputProps) => {
const modelsFoundLabel = isDirty ? (
'Save to see model length'
) : (
<button
className="link"
onClick={() => settingStore.setModelPanelOverride(connection.id)}
type="button"
>
<NavButtonDiv className="link" to={'/models/' + connection.id}>
{connection.models.length} models found
</button>
</NavButtonDiv>
)

return (
Expand All @@ -50,16 +47,15 @@ const HostInput = observer(({ connection, isEnabled }: HostInputProps) => {
<span className="flex flex-col gap-2 align-baseline text-sm md:flex-row">
<span className="flex align-baseline">
See connection instructions here:
<button
onClick={() => settingStore.openSettingsModal('connection')}
className="ml-2 align-baseline hover:text-base-content"
type="button"
<NavButtonDiv
to="/connection"
className="ml-2 flex items-center hover:text-base-content"
>
<Question />
</button>
</NavButtonDiv>
</span>

<span className="text-sm md:ml-auto md:pl-2">{modelsFoundLabel}</span>
<span className="text-right text-sm md:ml-auto md:pl-2">{modelsFoundLabel}</span>
</span>
}
endContent={
Expand Down
8 changes: 5 additions & 3 deletions src/components/ModelSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useMemo } from 'react'
import { observer } from 'mobx-react-lite'
import _ from 'lodash'
import { useNavigate } from 'react-router-dom'

import ChevronDown from '~/icons/ChevronDown'
import { settingStore } from '~/core/setting/SettingStore'
import { connectionStore } from '~/core/connection/ConnectionStore'

const ModelSelector = observer(() => {
const navigate = useNavigate()

const { selectedModelLabel, isAnyServerConnected, selectedConnection } = connectionStore
const noServer = !isAnyServerConnected

Expand All @@ -28,9 +30,9 @@ const ModelSelector = observer(() => {

const handleClick = () => {
if (!selectedConnection) {
settingStore.openSettingsModal('connections')
navigate('/models')
} else {
settingStore.openSettingsModal('models')
navigate('/models/' + selectedConnection.id)
}
}

Expand Down
46 changes: 46 additions & 0 deletions src/components/NavButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { MouseEventHandler } from 'react'
import { To, useNavigate } from 'react-router-dom'

type NavButtonProps<T = Element> = React.HTMLAttributes<T> & {
to: To
replace?: boolean
disabled?: boolean
}

export const NavButton = ({
to,
replace = false,
onClick,
...rest
}: NavButtonProps<HTMLButtonElement>) => {
const navigate = useNavigate()

const handleClick: MouseEventHandler<HTMLButtonElement> = e => {
onClick?.(e)

if (!e.isDefaultPrevented()) {
navigate(to, { replace })
}
}

return <button type="button" {...rest} onClick={handleClick} />
}

export const NavButtonDiv = ({
to,
replace = false,
onClick,
...rest
}: Omit<NavButtonProps<HTMLDivElement>, 'disabled'>) => {
const navigate = useNavigate()

const handleClick: MouseEventHandler<HTMLDivElement> = e => {
onClick?.(e)

if (!e.isDefaultPrevented()) {
navigate(to, { replace })
}
}

return <div role="button" {...rest} onClick={handleClick} />
}
10 changes: 3 additions & 7 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useKBar } from 'kbar'
import { Kbd } from '@nextui-org/react'
import { observer } from 'mobx-react-lite'
import { type PropsWithChildren } from 'react'
import { NavLink } from 'react-router-dom'

import ModelSelector from '~/components/ModelSelector'
import ModelRefreshButton from '~/components/ModelRefreshButton'
Expand All @@ -13,7 +14,6 @@ import Bars3 from '~/icons/Bars3'
import Search from '~/icons/Search'
import AppSettings from '~/icons/AppSettings'

import { settingStore } from '~/core/setting/SettingStore'
import { connectionStore } from '~/core/connection/ConnectionStore'

const KeyboardTooltip = ({ command, children }: PropsWithChildren<{ command: string }>) => (
Expand Down Expand Up @@ -69,11 +69,7 @@ const Navbar = observer(() => {
</KeyboardTooltip>

<KeyboardTooltip command="/">
<label
htmlFor="app-drawer"
className="btn btn-square btn-ghost btn-sm md:btn-md "
onClick={() => settingStore.openSettingsModal()}
>
<NavLink to="initial" className="btn btn-square btn-ghost btn-sm md:btn-md ">
<div className="indicator p-1">
<div className="swap lg:swap-active">
<div className="swap-on align-middle">
Expand All @@ -91,7 +87,7 @@ const Navbar = observer(() => {
</span>
)}
</div>
</label>
</NavLink>
</KeyboardTooltip>
</div>
</nav>
Expand Down
13 changes: 10 additions & 3 deletions src/components/OmniBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import type { Action, ActionImpl } from 'kbar'
import { autorun } from 'mobx'
import _ from 'lodash'
import { useNavigate } from 'react-router-dom'

import { settingStore } from '~/core/setting/SettingStore'
import { personaStore } from '~/core/persona/PersonaStore'
Expand Down Expand Up @@ -140,6 +141,8 @@ const useRegisterThemeActions = () => {
}

const useRegisterModelActions = () => {
const navigate = useNavigate()

const [modelActions, setModelActions] = useState<Action[]>([])

useEffect(() => {
Expand Down Expand Up @@ -203,7 +206,7 @@ const useRegisterModelActions = () => {
keywords: 'model modal open select',
section: 'Actions',
priority: Priority.LOW,
perform: () => settingStore.openSettingsModal('models'),
perform: () => navigate('/models'),
})

setModelActions(nextModelActions)
Expand All @@ -214,6 +217,8 @@ const useRegisterModelActions = () => {
}

const useRegisterPersonaActions = () => {
const navigate = useNavigate()

const [personaActions, setPersonaActions] = useState<Action[]>([])

useEffect(() => {
Expand All @@ -232,7 +237,7 @@ const useRegisterPersonaActions = () => {
keywords: 'persona open select',
section: 'Actions',
priority: Priority.LOW,
perform: () => settingStore.openSettingsModal('personas'),
perform: () => navigate('personas'),
},
]

Expand Down Expand Up @@ -425,6 +430,8 @@ const useNewChatActions = () => {
}

const OmniBar = () => {
const navigate = useNavigate()

useRegisterThemeActions()
useRegisterModelActions()
useRegisterPersonaActions()
Expand All @@ -446,7 +453,7 @@ const OmniBar = () => {
section: 'Actions',
shortcut: ['/'],
priority: Priority.LOW,
perform: () => settingStore.openSettingsModal('general'),
perform: () => navigate('/general'),
}),
])

Expand Down
2 changes: 1 addition & 1 deletion src/components/SelectionTablePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const SelectionPanelTable = observer(
return (
<span
className={
'transition-[opacity transform] ml-2 h-fit scale-90 duration-300 ease-in-out ' +
'transition-[opacity transform] ml-2 -mr-2 h-fit scale-90 duration-300 ease-in-out ' +
(ascendingSort ? ' rotate-180 ' : '') +
(activeSortType === sortType ? ' opacity-100 ' : ' opacity-0 ')
}
Expand Down
5 changes: 4 additions & 1 deletion src/components/form/FormInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ const FormInput = forwardRef((inputProps: FormInputProps) => {
inputWrapper:
'!bg-base-transparent border rounded-md border-base-content/30' +
(isInvalid ? ' !border-error' : '') +
(isDisabled ? ' opacity-30 hover:!border-base-content/30' : ''),
(isDisabled ? ' opacity-30 hover:!border-base-content/30' : '') +
(inputProps.variant === 'underlined' ? ' border-0 border-b-1' : ''),

input: inputProps.variant === 'underlined' ? ' !text-base-content' : '',
}}
{...inputProps}
/>
Expand Down
Loading

0 comments on commit 2b01d51

Please sign in to comment.