Skip to content

Commit

Permalink
Add tailwind plugin to prettier and reformat all files
Browse files Browse the repository at this point in the history
  • Loading branch information
mrdjohnson committed Feb 22, 2024
1 parent 3581e02 commit f60e45f
Show file tree
Hide file tree
Showing 31 changed files with 107 additions and 99 deletions.
5 changes: 1 addition & 4 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
}
2 changes: 1 addition & 1 deletion .github/workflows/deploy_to_gh_pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ jobs:
path: './dist'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v2
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "avoid",
"printWidth": 100
"printWidth": 100,
"plugins": ["prettier-plugin-tailwindcss"]
}
31 changes: 16 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,42 @@

![LLM X logo](https://raw.githubusercontent.com/mrdjohnson/llm-X/main/public/LLMX.png)

***What is this?***
**_What is this?_**
Chat GPT style UI for the niche group of folks who run [Ollama](https://ollama.com/) (think of this like an offline chat gpt) locally. Supports sending images and text!
**WORKS OFFLINE** through PWA ([Progressive Web App](https://web.dev/explore/progressive-web-apps)) standards (its not dead!)

***Why do this?***
**_Why do this?_**
I have been interested in **LLM UI** for a while now and this seemed like a good intro application.
I've been introduced to a lot of modern technologies thanks to this project as well, its been fun!
I've been introduced to a lot of modern technologies thanks to this project as well, its been fun!

***Why so many buzz words?***
I couldn't help but bee cool 😎
**_Why so many buzz words?_**
I couldn't help but bee cool 😎

## Tech Stack (thank you's):

**_Logic helpers:_**


## Tech Stack (thank you's):
***Logic helpers:***
- [React](https://react.dev/)
- [Typescript](https://www.typescriptlang.org/)
- [Mobx State Tree](https://mobx-state-tree.js.org/intro/welcome)

***UI Helpers:***
**_UI Helpers:_**

- [Tailwind css](https://tailwindcss.com/)
- [DaisyUI](https://daisyui.com/)
- [Highlight.js](https://www.npmjs.com/package/highlight.js)
- [React Markdown](https://www.npmjs.com/package/react-markdown)

***Project setup helpers:***
**_Project setup helpers:_**

- [Vite](https://vitejs.dev/)
- [Vite PWA plugin](https://vite-pwa-org.netlify.app/)


***Inspiration:***
[ollama-ui's](https://github.com/ollama-ui/ollama-ui) project. Which allows users to connect to ollama via a [web app](https://ollama-ui.github.io/ollama-ui/)
**_Inspiration:_**
[ollama-ui's](https://github.com/ollama-ui/ollama-ui) project. Which allows users to connect to ollama via a [web app](https://ollama-ui.github.io/ollama-ui/)

[Perplexity.ai](https://www.perplexity.ai/) Perpexlity has sone some amazing UI advancements in the LLM UI space and I have been very interested in getting to that point. Hopefully this starter project lets me get closer to doing something similar!


## Getting started

Clone the project, and run `npm install` in the root directory
Expand Down Expand Up @@ -66,4 +67,4 @@ Clone the project, and run `npm install` in the root directory

- This readme was written with [https://stackedit.io/app](https://stackedit.io/app)

- Changes to the main branch trigger an immediate deploy to https://mrdjohnson.github.io/llm-x/
- Changes to the main branch trigger an immediate deploy to https://mrdjohnson.github.io/llm-x/
4 changes: 2 additions & 2 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

html, body {
html,
body {
min-height: 100% !important;
height: 100%;
}
Expand Down
11 changes: 5 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const Navbar = observer(() => {
const noServer = !settingStore.selectedModel

return (
<nav className="navbar bg-base-300 rounded-md mb-2">
<nav className="navbar mb-2 rounded-md bg-base-300">
<div className="navbar-start text-xl">
<label className="ml-2 text-xl">LLM X</label>
</div>
Expand All @@ -32,7 +32,6 @@ const Navbar = observer(() => {
<ModelRefreshButton />
</div>


<div className="navbar-end">
<label htmlFor="app-drawer" className="btn btn-square btn-ghost drawer-button ">
<div className="indicator p-1">
Expand All @@ -53,7 +52,7 @@ const Navbar = observer(() => {
function App() {
return (
<div className="App grid">
<div className="flex flex-col max-h-screen p-3 drawer drawer-end place-self-center container">
<div className="container drawer drawer-end flex max-h-screen flex-col place-self-center p-3">
<Navbar />

<Drawer />
Expand All @@ -64,12 +63,12 @@ function App() {

<PwaReloadPrompt />

<section className="flex flex-row gap-4 text-xl flex-grow max-h-full overflow-hidden h-screen drawer-content w-full">
<aside className="h-full hidden lg:block">
<section className="drawer-content flex h-screen max-h-full w-full flex-grow flex-row gap-4 overflow-hidden text-xl">
<aside className="hidden h-full lg:block">
<SideBar />
</aside>

<main className=" h-full flex-1 w-full overflow-x-auto overflow-y-scroll">
<main className=" h-full w-full flex-1 overflow-x-auto overflow-y-scroll">
<ChatBox />
</main>
</section>
Expand Down
18 changes: 9 additions & 9 deletions src/components/ChatBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ const ChatBoxInputRow = observer(
const inputDisabled = chatStore.isGettingData || noServer

return (
<div className={'w-full mt-2 ' + (noServer && 'tooltip')} data-tip="Server is not connected">
<form className="flex flex-row min-h-fit gap-2 w-full" onSubmit={onFormSubmit}>
<div className="join w-full flex-1 relative">
<div className={'mt-2 w-full ' + (noServer && 'tooltip')} data-tip="Server is not connected">
<form className="flex min-h-fit w-full flex-row gap-2" onSubmit={onFormSubmit}>
<div className="join relative w-full flex-1">
<input
className="input input-bordered join-item grow focus:outline-none"
className="input join-item input-bordered grow focus:outline-none"
placeholder="Enter Prompt"
ref={inputRef}
type="text"
Expand All @@ -81,7 +81,7 @@ const ChatBoxInputRow = observer(
<button
className={
'btn join-item !rounded-r-md border ' +
(inputDisabled ? '': 'input-bordered hover:input-bordered')
(inputDisabled ? '' : 'input-bordered hover:input-bordered')
}
type="button"
onClick={() => fileInputRef.current?.click()}
Expand All @@ -91,10 +91,10 @@ const ChatBoxInputRow = observer(
</button>

{previewImage && (
<div className="h-24 w-24 absolute --top-full end-0 bottom-full mb-2">
<div className="--top-full absolute bottom-full end-0 mb-2 h-24 w-24">
<div className="relative h-full w-full">
<div
className="absolute top-1 right-1 opacity-70 btn btn-xs"
className="btn btn-xs absolute right-1 top-1 opacity-70"
onClick={() => setPreviewImage(undefined)}
>
x
Expand Down Expand Up @@ -171,10 +171,10 @@ const ChatBox = observer(() => {
}

return (
<div className="rounded-md flex flex-col min-h-full max-h-full w-full max-w-full overflow-x-auto overflow-y-scroll min-w-full">
<div className="flex max-h-full min-h-full w-full min-w-full max-w-full flex-col overflow-x-auto overflow-y-scroll rounded-md">
<ScrollableFeed
ref={scrollableFeedRef}
className="flex flex-col gap-2 flex-1 no-scrollbar overflow-x-hidden overflow-y-scroll"
className="no-scrollbar flex flex-1 flex-col gap-2 overflow-x-hidden overflow-y-scroll"
animateScroll={(element, offset) => element.scrollBy({ top: offset, behavior: 'smooth' })}
>
{chat.messages.map(message => (
Expand Down
10 changes: 5 additions & 5 deletions src/components/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ const Input = observer(() => {
return (
<div className="form-control">
<div className="label pb-1 pt-0">
<span className="label-text text-sm flex flex-row items-center gap-2">
<span className="label-text flex flex-row items-center gap-2 text-sm">
Host:
<div className="cursor-pointer" onClick={openNoServerDialog}>
<Question />
</div>
</span>
</div>

<div className="flex flex-row gap-2 items-center">
<div className="flex flex-row items-center gap-2">
<input
type="text"
id="host"
Expand Down Expand Up @@ -66,9 +66,9 @@ const Drawer = () => {
<section className="drawer-side z-20">
<label htmlFor="app-drawer" aria-label="close sidebar" className="drawer-overlay" />

<div className="p-3 w-80 min-h-full bg-base-200 flex flex-col">
<div className="flex min-h-full w-80 flex-col bg-base-200 p-3">
<div className="navbar" />
<div className="flex flex-col gap-3 flex-1 h-full">
<div className="flex h-full flex-1 flex-col gap-3">
<Input />

<Suspense fallback={<div> Loading models ... </div>}>
Expand All @@ -77,7 +77,7 @@ const Drawer = () => {

<ThemeSelector />

<div className="lg:hidden h-full flex-1 flex bg-base-300 rounded-md flex">
<div className="flex h-full flex-1 rounded-md bg-base-300 lg:hidden">
<SideBar />
</div>
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/components/HelpModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ const HelpModal = observer(() => {

return (
<dialog ref={modalRef} id="help-modal" className="modal modal-top">
<div className="modal-box w-9/12 rounded place-self-center max-w-[1000px]">
<h3 className="font-bold text-xl pb-3">How to connect to Ollama Server:</h3>
<div className="modal-box w-9/12 max-w-[1000px] place-self-center rounded">
<h3 className="pb-3 text-xl font-bold">How to connect to Ollama Server:</h3>

<div className="flex flex-col gap-2">
<p>By default, Ollama allows cross origin requests from 127.0.0.1 and 0.0.0.0.</p>{' '}
Expand All @@ -47,13 +47,13 @@ const HelpModal = observer(() => {
</p>
</div>

<div className="flex flex-row place-content-center gap-2 my-4">
<div className="my-4 flex flex-row place-content-center gap-2">
<div className="prose">
<code>{OLLAMA_CODE}</code>
</div>

<label
className={'swap btn btn-neutral btn-sm ' + (copied && 'swap-active')}
className={'btn swap btn-neutral btn-sm ' + (copied && 'swap-active')}
onClick={handleCopy}
>
<Copy className="swap-off" />
Expand All @@ -70,7 +70,7 @@ const HelpModal = observer(() => {

<div className="modal-action">
<form method="dialog">
<button className="btn btn-sm btn-ghost focus:outline-0 absolute right-2 top-2">
<button className="btn btn-ghost btn-sm absolute right-2 top-2 focus:outline-0">
</button>
</form>
Expand Down
20 changes: 11 additions & 9 deletions src/components/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const customCodeBlock = (props: React.HTMLAttributes<HTMLElement>) => {
return (
<div className="indicator w-full ">
<button
className="text-neutral-content/30 indicator-item hover:text-neutral-content z-10 "
className="indicator-item z-10 text-neutral-content/30 hover:text-neutral-content "
onClick={copy}
>
<Copy />
Expand All @@ -40,7 +40,7 @@ const customCodeBlock = (props: React.HTMLAttributes<HTMLElement>) => {
<code
{...rest}
dangerouslySetInnerHTML={{ __html: highlightedText }}
className="w-full overflow-x-scroll max-w-lg xl:max-w-[700px] 2xl:max-w-[1000px]"
className="w-full max-w-lg overflow-x-scroll xl:max-w-[700px] 2xl:max-w-[1000px]"
/>
</div>
)
Expand All @@ -50,7 +50,7 @@ const customCodeBlock = (props: React.HTMLAttributes<HTMLElement>) => {
}

const Loading = () => (
<span className="indicator-item indicator-start loading loading-dots loading-sm ml-8" />
<span className="indicator-item loading loading-dots loading-sm indicator-start ml-8" />
)

// this one is observed for incoming text changes, the rest do not need to be observed
Expand Down Expand Up @@ -92,13 +92,15 @@ export const Message = ({ message, onDestroy, children, customDeleteIcon }: Mess
return (
<div
className={
'group w-fit max-w-full flex flex-col indicator ' + (fromBot ? 'pr-6 ' : ' ml-2 self-end ')
'group indicator flex w-fit max-w-full flex-col ' + (fromBot ? 'pr-6 ' : ' ml-2 self-end ')
}
key={uniqId}
>
{image && <img className="w-56 h-56 rounded-md place-self-center object-contain" src={image} />}
{image && (
<img className="h-56 w-56 place-self-center rounded-md object-contain" src={image} />
)}

<div className=" border border-base-content/20 p-2 rounded-md w-full">
<div className=" w-full rounded-md border border-base-content/20 p-2">
<Markdown
remarkPlugins={[[remarkGfm, { singleTilde: false }]]}
className="prose inline-table w-full"
Expand All @@ -112,13 +114,13 @@ export const Message = ({ message, onDestroy, children, customDeleteIcon }: Mess

{children}

<div className={'group-hover:opacity-90 opacity-0 w-fit mt-1 ' + (!fromBot && 'self-end')}>
<button className="mr-2 hover:text-error text-error/30 rounded-md" onClick={onDestroy}>
<div className={'mt-1 w-fit opacity-0 group-hover:opacity-90 ' + (!fromBot && 'self-end')}>
<button className="mr-2 rounded-md text-error/30 hover:text-error" onClick={onDestroy}>
{customDeleteIcon || <Delete />}
</button>

<button
className="hover:text-base-content text-base-content/30 rounded-md"
className="rounded-md text-base-content/30 hover:text-base-content"
onClick={handleCopy}
>
<label className={'swap ' + (copied && 'swap-active')}>
Expand Down
18 changes: 13 additions & 5 deletions src/components/ModalSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,25 @@ const ModalSelector = observer(() => {

return (
<div className="dropdown">
<button tabIndex={0} role="button" className="btn btn-active w-full" disabled={!selectedModel}>
<button
tabIndex={0}
role="button"
className="btn btn-active w-full"
disabled={!selectedModel}
>
{selectedModel?.name || 'No models available'}
<ChevronDown />
</button>

<ul
tabIndex={0}
className="dropdown-content z-[1] p-2 shadow-2xl bg-base-300 rounded-box mt-2 border border-base-content/30 max-h-52 flex flex-col"
className="dropdown-content z-[1] mt-2 flex max-h-52 flex-col rounded-box border border-base-content/30 bg-base-300 p-2 shadow-2xl"
>
{models?.map(model => (
<li key={model.name}>
<input
type="radio"
className="btn btn-sm btn-ghost w-full place-items-center"
className="btn btn-ghost btn-sm w-full place-items-center"
aria-label={model.name}
value={model.name}
checked={selectedModel === model}
Expand All @@ -33,8 +38,11 @@ const ModalSelector = observer(() => {
))}

<li>
<a href="https://ollama.com/library" className="btn btn-outline btn-sm btn-neutral place-items-center flex flex-row flex-nowrap gap-2 w-full mt-2">
<span className=" text-sm whitespace-nowrap ">Ollama Library</span>
<a
href="https://ollama.com/library"
className="btn btn-outline btn-neutral btn-sm mt-2 flex w-full flex-row flex-nowrap place-items-center gap-2"
>
<span className=" whitespace-nowrap text-sm ">Ollama Library</span>
<Globe />
</a>
</li>
Expand Down
16 changes: 8 additions & 8 deletions src/components/PwaReloadPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,19 @@ const PwaReloadPrompt = () => {

return (
<dialog id="pwa_refresh_modal" className="modal modal-top">
<div className="modal-box justify-self-center m-8 rounded-md max-w-[600px] flex flex-row items-center w-fit relative">
<h3 className="font-bold text-lg">Updates Found, Please refresh the page</h3>
<div className="modal-box relative m-8 flex w-fit max-w-[600px] flex-row items-center justify-self-center rounded-md">
<h3 className="text-lg font-bold">Updates Found, Please refresh the page</h3>

<div className="btn btn-neutral mx-4 btn-sm" onClick={() => updateServiceWorker(true)}>
<div className="btn btn-neutral btn-sm mx-4" onClick={() => updateServiceWorker(true)}>
Refresh
</div>

<div
className="absolute top-1 right-1 opacity-50 btn btn-xs font-bold text-sm"
onClick={() => setNeedRefresh(false)}
>
x
</div>
className="btn btn-xs absolute right-1 top-1 text-sm font-bold opacity-50"
onClick={() => setNeedRefresh(false)}
>
x
</div>
</div>

<form method="dialog" className="modal-backdrop">
Expand Down
Loading

0 comments on commit f60e45f

Please sign in to comment.