diff --git a/package.json b/package.json index 7fd1536..3d36757 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chat-with-gpt", - "version": "0.1.0", + "version": "0.1.1", "dependencies": { "@emotion/css": "^11.10.6", "@emotion/styled": "^11.10.6", diff --git a/src/chat-manager.ts b/src/chat-manager.ts index 10d7506..f04720d 100644 --- a/src/chat-manager.ts +++ b/src/chat-manager.ts @@ -8,6 +8,7 @@ import { createStreamingChatCompletion } from './openai'; import { createTitle } from './titles'; import { ellipsize, sleep } from './utils'; import * as idb from './idb'; +import { getTokenCountForMessages, selectMessagesToSendSafely } from './tokenizer'; export const channel = new BroadcastChannel('chats'); @@ -102,9 +103,10 @@ export class ChatManager extends EventEmitter { ? chat.messages.getMessageChainTo(message.parentID) : []; messages.push(newMessage); + + const messagesToSend = selectMessagesToSendSafely(messages.map(getOpenAIMessageFromMessage)); - const response = await createStreamingChatCompletion(messages.map(getOpenAIMessageFromMessage), - message.requestedParameters); + const response = await createStreamingChatCompletion(messagesToSend, message.requestedParameters); response.on('error', () => { if (!reply.content) { @@ -250,7 +252,7 @@ export class Search { if (!chat.title) { chat.title = ellipsize(description, 100); } - + if (!chat.title || !description) { continue; } diff --git a/src/components/header.tsx b/src/components/header.tsx index d116d67..1ac8c64 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -1,15 +1,17 @@ import styled from '@emotion/styled'; import Helmet from 'react-helmet'; import { useSpotlight } from '@mantine/spotlight'; -import { Button, ButtonProps, TextInput } from '@mantine/core'; +import { Button, ButtonProps } from '@mantine/core'; import { useCallback, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { APP_NAME } from '../values'; import { useAppContext } from '../context'; import { backend } from '../backend'; +import { MenuItem, primaryMenu, secondaryMenu } from '../menus'; -const Container = styled.div` +const HeaderContainer = styled.div` display: flex; + flex-shrink: 0; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; @@ -56,7 +58,44 @@ const Container = styled.div` font-size: 90%; } - i + span { + i + span, .mantine-Button-root span.hide-on-mobile { + @media (max-width: 40em) { + position: absolute; + left: -9999px; + top: -9999px; + } + } + + .mantine-Button-root { + @media (max-width: 40em) { + padding: 0.5rem; + } + } +`; + +const SubHeaderContainer = styled.div` + display: flex; + flex-direction: row; + font-family: "Work Sans", sans-serif; + line-height: 1.7; + font-size: 80%; + opacity: 0.7; + margin: 0.5rem 1rem 0 1rem; + gap: 1rem; + + .spacer { + flex-grow: 1; + } + + a { + color: white; + } + + .fa { + font-size: 90%; + } + + .fa + span { @media (max-width: 40em) { position: absolute; left: -9999px; @@ -68,8 +107,8 @@ const Container = styled.div` function HeaderButton(props: ButtonProps & { icon?: string, onClick?: any, children?: any }) { return ( + ); +} + +export function SubHeader(props: any) { + return + {primaryMenu.map(item => )} +
+ {secondaryMenu.map(item => )} + ; } \ No newline at end of file diff --git a/src/components/input.tsx b/src/components/input.tsx index 8ed0d55..d13eaca 100644 --- a/src/components/input.tsx +++ b/src/components/input.tsx @@ -1,17 +1,13 @@ import styled from '@emotion/styled'; import { Button, ActionIcon, Textarea } from '@mantine/core'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; import { useAppContext } from '../context'; -import { Parameters } from '../types'; const Container = styled.div` background: #292933; border-top: thin solid #393933; padding: 1rem 1rem 0 1rem; - position: absolute; - bottom: 0rem; - left: 0; - right: 0; .inner { max-width: 50rem; @@ -30,10 +26,10 @@ export declare type OnSubmit = (name?: string) => Promise; function PaperPlaneSubmitButton(props: { onSubmit: any, disabled?: boolean }) { return ( - props.onSubmit()}> + ); @@ -41,27 +37,24 @@ function PaperPlaneSubmitButton(props: { onSubmit: any, disabled?: boolean }) { export interface MessageInputProps { disabled?: boolean; - parameters: Parameters; - onSubmit: OnSubmit; } export default function MessageInput(props: MessageInputProps) { const context = useAppContext(); - - const [message, setMessage] = useState(''); + const pathname = useLocation().pathname; const onChange = useCallback((e: React.ChangeEvent) => { - setMessage(e.target.value); - }, []); + context.setMessage(e.target.value); + }, [context.setMessage]); const onSubmit = useCallback(async () => { - if (await props.onSubmit(message)) { - setMessage(''); + if (await context.onNewMessage(context.message)) { + context.setMessage(''); } - }, [message, props.onSubmit]); + }, [context.message, context.onNewMessage, context.setMessage]); const onKeyDown = useCallback((e: React.KeyboardEvent) => { - if (e.key === 'Enter'&& e.shiftKey === false && !props.disabled) { + if (e.key === 'Enter' && e.shiftKey === false && !props.disabled) { e.preventDefault(); onSubmit(); } @@ -81,31 +74,41 @@ export default function MessageInput(props: MessageInputProps) { const openSystemPromptPanel = useCallback(() => context.settings.open('options', 'system-prompt'), []); const openTemperaturePanel = useCallback(() => context.settings.open('options', 'temperature'), []); + const messagesToDisplay = context.currentChat.messagesToDisplay; + const disabled = context.generating + || messagesToDisplay[messagesToDisplay.length - 1]?.role === 'user' + || (messagesToDisplay.length > 0 && !messagesToDisplay[messagesToDisplay.length - 1]?.done); + + const isLandingPage = pathname === '/'; + if (context.isShare || (!isLandingPage && !context.id)) { + return null; + } + return
-