Skip to content

Commit

Permalink
ui: update look and feel of chatbox prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
mrdjohnson committed Dec 9, 2024
1 parent 69911b6 commit 4ce088b
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 142 deletions.
170 changes: 39 additions & 131 deletions src/components/ChatBoxPrompt.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,38 @@
import { PropsWithChildren, useEffect, useMemo, useState } from 'react'
import _ from 'lodash'
import { observer } from 'mobx-react-lite'
import { type To } from 'react-router-dom'
import { twMerge } from 'tailwind-merge'
import _ from 'lodash'

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'

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

const Step = ({
isCompleted,
type = 'primary',
children,
inCompleteIcon = '●',
}: PropsWithChildren<StepProps>) => {
return (
<li
className={twMerge('step', isCompleted && `step-${type}`)}
data-content={isCompleted ? '✓' : inCompleteIcon}
import { NavButton } from '~/components/NavButton'

type PromptButtonProps = { title: string; link?: To }

const direction = _.sample([
'bg-gradient-to-br',
'bg-gradient-to-tr',

'bg-gradient-to-bl',
'bg-gradient-to-tl',
])

const PromptButton = ({ title, link }: PromptButtonProps) => (
<div
className={twMerge(
'group m-[2px] rounded-full from-primary/30 to-secondary/60 p-[1px] pt-[1px] transition-all duration-150 ease-in-out hover:m-[0px] hover:p-[3px]',
direction,
)}
>
<NavButton
to={link}
className="btn btn-ghost rounded-full bg-base-100 text-base-content/80 group-hover:!bg-base-100 group-hover:text-base-content"
>
<span className="text-left">{children}</span>
</li>
)
}

const ChatBoxPrompt = observer(({ chat }: { chat: ChatViewModel }) => {
const [hasCreatedPersonas, setHasCreatedPersonas] = useState(false)

const connections = connectionStore.connections

const activeConnectionTypes = useMemo(() => {
const connectionsTypes = _.chain(connections).filter('isConnected').map('type').value()

return new Set(connectionsTypes)
}, [connections])

const anyConnectionHasModels = useMemo(() => {
for (const connection of connections) {
if (!_.isEmpty(connection.models)) {
return true
}
}
return false
}, [connections])

useEffect(() => {
if (personaTable.cache.length > 0) {
setHasCreatedPersonas(true)
return
}

personaTable.length().then(length => {
if (length > 0) {
setHasCreatedPersonas(true)
}
})
}, [])
<p className="first-letter:text-semibold">{title}</p>
</NavButton>
</div>
)

const ChatBoxPrompt = () => {
return (
<div className="hero my-auto">
<div className="hero-content w-fit text-center">
Expand All @@ -72,86 +43,23 @@ const ChatBoxPrompt = observer(({ chat }: { chat: ChatViewModel }) => {
<FunTitle className="text-2xl font-bold md:text-4xl" />
</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">
<Step isCompleted={!_.isEmpty(activeConnectionTypes)}>
{'Tell LM Studio, Ollama, AUTOMATIC1111, or Open AI that '}
<span className="text-primary">we're cool:</span>
<NavButtonDiv
to="/connection"
className="link inline-block text-lg decoration-primary"
>
How to connect
</NavButtonDiv>
</Step>

<Step isCompleted={anyConnectionHasModels}>
{'Download a model from '}
<a
href="https://huggingface.co/lmstudio-community"
className="link decoration-primary"
target="__blank"
title="Open LM Studio's hugging face account in new tab"
>
LM Studio's Hugging face
</a>
{' or '}
<a
href="https://ollama.com/library"
className="link decoration-primary"
target="__blank"
title="Open Ollama Library in new tab"
>
Ollama Library
</a>
</Step>

<Step type="secondary" isCompleted={hasCreatedPersonas}>
{'Create and Select a'}

<NavButtonDiv
to="/personas"
className="link ml-1 inline-block decoration-secondary"
>
Persona <span className="text-xs">(aka System Prompt)</span>
</NavButtonDiv>
<div className="mt-8 flex flex-row flex-wrap justify-center gap-2 text-2xl">
<PromptButton title="Select a model" link="/models" />

{'to give your bot some pizzaz'}
</Step>
<PromptButton title="Create a system prompt" link="/personas" />

<Step type="secondary">
{'Drag and Drop or'}
<AttachmentWrapper>
<PromptButton title="Attach images" />
</AttachmentWrapper>

<AttachmentWrapper accept=".json">
<span className="link decoration-secondary">Import a chat or save</span>
</AttachmentWrapper>

{'from a previous session'}
</Step>

<Step type="secondary" isCompleted={!_.isEmpty(chat?.previewImages)}>
{'Drag and Drop, Paste, or'}

<AttachmentWrapper>
<span className="link decoration-secondary">
Attach as many images as you want
</span>
</AttachmentWrapper>

{'for use with multimodal models'}
</Step>

<Step inCompleteIcon="★">
<span>
<span className="font-semibold text-primary">Send</span> a prompt!
</span>
</Step>
</ul>
<AttachmentWrapper accept=".json">
<PromptButton title="Import previous chat" />
</AttachmentWrapper>
</div>
</div>
</div>
</div>
)
})
}

export default ChatBoxPrompt
6 changes: 3 additions & 3 deletions src/components/NavButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MouseEventHandler } from 'react'
import { To, useNavigate } from 'react-router-dom'

type NavButtonProps<T = Element> = React.HTMLAttributes<T> & {
to: To
to?: To
replace?: boolean
disabled?: boolean
}
Expand All @@ -18,7 +18,7 @@ export const NavButton = ({
const handleClick: MouseEventHandler<HTMLButtonElement> = e => {
onClick?.(e)

if (!e.isDefaultPrevented()) {
if (to && !e.isDefaultPrevented()) {
navigate(to, { replace })
}
}
Expand All @@ -37,7 +37,7 @@ export const NavButtonDiv = ({
const handleClick: MouseEventHandler<HTMLDivElement> = e => {
onClick?.(e)

if (!e.isDefaultPrevented()) {
if (to && !e.isDefaultPrevented()) {
navigate(to, { replace })
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/containers/ChatBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const ChatBox = observer(() => {
/>
))
) : (
<ChatBoxPrompt chat={chat} />
<ChatBoxPrompt />
)}
</ScrollableChatFeed>

Expand Down
3 changes: 1 addition & 2 deletions src/core/connection/api/LmsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const getMessages = async (chatMessages: MessageViewModel[], chatMessageId: stri
}

export class LmsApi extends BaseApi {
// @ts-expect-error todo: fix broken lm studio sdk (again) at another time
async *generateChat(chatMessages: MessageViewModel[], incomingMessageVariant: MessageViewModel) {
const connection = incomingMessageVariant.actor.connection
const host = connection?.formattedHost
Expand All @@ -68,7 +67,7 @@ export class LmsApi extends BaseApi {
BaseApi.abortControllerById[incomingMessageVariant.id] = () => prediction.cancel()

for await (const text of prediction) {
yield text
yield text.content
}

const { stats } = await prediction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ const ConnectionPanel = observer(() => {
className="btn btn-outline btn-sm mr-8 w-full text-error md:btn-ghost md:text-error"
onClick={deleteConnection}
>
Delete Connection
Delete Provider
</button>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ const NewConnectionPanel = observer(() => {
}

return (
<Drawer label="Add new connection">
<Drawer label="Add Model Provider">
<div className="mx-auto mt-4 flex flex-col gap-4 *:text-center">
<span>Select a connection type: </span>
<span>Select a model provider: </span>

{_.map(connectionViewModelByType, getConnector => getConnector().getSnapshot()).map(
({ type, label }) => (
Expand Down
2 changes: 1 addition & 1 deletion src/features/settings/panels/help/LmsHelpMarkdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ LM Studio makes working with models easy! Use this and get going:
\`${LMS_CODE}\`
Find models to download on their [Hugging Face](https://huggingface.co/lmstudio-community?sort_models=likes#models)
Find models to download at their [Hugging Face](https://huggingface.co/lmstudio-community?sort_models=likes#models)
`
2 changes: 2 additions & 0 deletions src/features/settings/panels/help/OllamaHelpMarkdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ By default, Ollama only allows requests from local host. To use custom origins (
- Powershell version: \`${POWERSHELL_OLLAMA_CODE}\`
Find out more about Ollama on their website: [https://ollama.com/](https://ollama.com/)
Find models to download at their [library](https://ollama.com/library)
`
2 changes: 1 addition & 1 deletion src/features/settings/panels/model/ModelPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const ModelPanel = observer(() => {
emptyLabel: 'No connections found',
}}
addButtonProps={{
label: 'Add New Connection',
label: 'Add Model Provider',
}}
selectedItemId={selectedConnection?.id}
renderActionRow={connection => (
Expand Down

0 comments on commit 4ce088b

Please sign in to comment.