Exploratory client

This commit is contained in:
Jeffrey Morgan 2023-06-23 14:04:39 -04:00
parent db81d81b23
commit 2f4aa42d2e
8 changed files with 6605 additions and 89 deletions

View file

@ -6,3 +6,7 @@ html,
body {
background: transparent;
}
.drag {
-webkit-app-region: drag;
}

View file

@ -25,14 +25,7 @@ interface RootLayoutProps {
export default function RootLayout({ children }: RootLayoutProps) {
return (
<html lang='en' suppressHydrationWarning>
<head />
<body className='font-sans antialiased min-h-screen flex'>
<aside className='w-52 flex-none'></aside>
<section className='flex-1 bg-white border-l border-gray-300'>
<header className='sticky top-0 z-50 flex h-16 w-full shrink-0 items-center justify-between px-4 backdrop-blur-xl'></header>
<section className='flex flex-col flex-1'>{children}</section>
</section>
</body>
<body className='flex min-h-screen w-full bg-white font-sans antialiased'>{children}</body>
</html>
)
}

View file

@ -1,21 +1,112 @@
'use client'
import { useState } from 'react'
const API_URL = 'http://127.0.0.1:8080'
type Message = {
sender: string
content: string
}
async function completion(prompt: string, callback: (res: string) => void) {
const result = await fetch(`${API_URL}/completion`, {
method: 'POST',
body: JSON.stringify({
prompt: prompt,
temperature: 0.2,
top_k: 40,
top_p: 0.9,
n_predict: 256,
stop: ['\n### Human:'], // stop completion after generating this
stream: true,
}),
})
if (!result.ok || !result.body) {
return
}
let reader = result.body.getReader()
while (true) {
const { done, value } = await reader.read()
if (done) {
break
}
const t = Buffer.from(value).toString('utf8')
if (t.startsWith('data: ')) {
const message = JSON.parse(t.substring(6))
callback(message.content)
if (message.stop) {
break
}
}
}
return
}
export default function Home() {
const [prompt, setPrompt] = useState('')
const [messages, setMessages] = useState<Message[]>([])
return (
<div className='flex min-h-screen flex-col items-center justify-between p-24'>
hello
<textarea
autoFocus
rows={1}
className='w-full border border-gray-200 rounded-xl px-5 py-3.5 resize-none text-[15px] shadow-lg shadow-black/5 block mx-4 focus:outline-none'
onKeyDownCapture={e => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault() // Prevents the newline character from being inserted
// Perform your desired action here, such as submitting the form or handling the entered text
console.log('Enter key pressed!')
}
}}
></textarea>
<div className='flex min-h-screen flex-1 flex-col justify-between'>
<header className='drag sticky top-0 z-50 flex w-full flex-row items-center border-b border-black/5 bg-gray-50/75 p-3 backdrop-blur-md'>
<div className='mx-auto w-full max-w-xl leading-none'>
<h1 className='text-sm font-medium'>LLaMa</h1>
<h2 className='text-xs text-black/50'>Meta Platforms, Inc.</h2>
</div>
</header>
<section className='mx-auto mb-10 w-full max-w-xl flex-1 break-words'>
{messages.map((m, i) => (
<div className='my-4 flex gap-4' key={i}>
<div className='flex-none pr-2 text-lg'>{m.sender === 'human' ? '👩' : '🤖'}</div>
<div className='flex-1 text-gray-900'>
{m.content}
{m.sender === 'bot' && <span className='relative -top-[3px] left-1 text-[10px] text-blue-600'></span>}
</div>
</div>
))}
</section>
<div className='sticky bottom-0 bg-gradient-to-b from-transparent to-white'>
<textarea
autoFocus
rows={1}
value={prompt}
placeholder='Send a message...'
onChange={e => setPrompt(e.target.value)}
className='mx-auto my-4 block w-full max-w-xl resize-none rounded-xl border border-gray-200 px-5 py-3.5 text-[15px] shadow-lg shadow-black/5 focus:outline-none'
onKeyDownCapture={async e => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault() // Prevents the newline character from being inserted
// Perform your desired action here, such as submitting the form or handling the entered text
await setMessages(messages => {
return [...messages, { sender: 'human', content: prompt }]
})
const index = messages.length + 1
completion(prompt, res => {
setMessages(messages => {
let message = messages[index]
if (!message) {
message = { sender: 'bot', content: '' }
}
message.content = message.content + res
return [...messages.slice(0, index), message]
})
})
setPrompt('')
}
}}
></textarea>
</div>
</div>
)
}

526
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -27,12 +27,14 @@
"@types/node": "20.3.1",
"@types/react": "18.2.13",
"@types/react-dom": "18.2.6",
"pkg": "^5.8.1",
"tailwindcss": "3.3.2",
"typescript": "5.1.3",
"postcss": "8.4.24",
"autoprefixer": "10.4.14",
"eslint": "8.43.0",
"eslint-config-next": "13.4.7"
"eslint-config-next": "13.4.7",
"pkg": "^5.8.1",
"postcss": "8.4.24",
"prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.3.0",
"tailwindcss": "3.3.2",
"typescript": "5.1.3"
}
}

6013
desktop/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -11,9 +11,11 @@ const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
titleBarStyle: 'hidden',
trafficLightPosition: { x: 20, y: 18 },
vibrancy: 'titlebar',
minWidth: 400,
minHeight: 300,
titleBarStyle: 'hiddenInset',
// trafficLightPosition: { x: 20, y: 18 },
// vibrancy: 'titlebar',
transparent: true,
})

3
dev.sh
View file

@ -2,7 +2,7 @@
# Function to handle Ctrl+C
handle_sigint() {
kill $pid1 $pid2 $pid3
kill $pid1 $pid2
exit
}
@ -12,7 +12,6 @@ trap 'handle_sigint' SIGINT
# Start three processes in the background
npm run dev --prefix ./client & pid1=$!
npm start --prefix ./desktop & pid2=$!
go run -C ./server . & pid3=$!
# Wait for all processes to finish
wait