diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index a13069d..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "rules": { - "no-restricted-globals": ["error", "event", "fdescribe"] - } -} \ No newline at end of file diff --git a/.gitignore b/.gitignore deleted file mode 100644 index ebf69e5..0000000 --- a/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# auto generated -*.lock -*-lock.json diff --git a/404.html b/404.html new file mode 100644 index 0000000..3b95ae0 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ +Ollama GUI
\ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 0f5acd9..0000000 --- a/README.md +++ /dev/null @@ -1,66 +0,0 @@ -## Ollama LLM Graphical User Interface - -> 👨‍💻 Developed by Matheus Ramalho de Oliveira -🏗️ Brazilian Software Engineer -✉️ kastorcode@gmail.com -🦫 [LinkedIn](https://br.linkedin.com/in/kastorcode) • [Instagram](https://instagram.com/kastorcode) - ---- - -

- -

- -

- This application is a frontend for the LLM (large language model) Ollama. Ollama is an interface created by Meta that facilitates the use of artificial intelligence. -

- ---- - -### Screenshots - -

- - - - - - -

- ---- - -### Technologies -[Craco](https://craco.js.org) -[Flux Architecture](https://facebookarchive.github.io/flux) -[React.js](https://react.dev) -[React Hooks Global State](https://npmjs.com/package/react-hooks-global-state) -[React Router](https://reactrouter.com) -[React Transition Group](https://reactcommunity.org/react-transition-group) -[Styled Components](https://styled-components.com) -[TypeScript](https://typescriptlang.org) - ---- - -### Installation and execution - -1. You need to have the [Ollama server](https://ollama.com/download) installed on your machine, or configure the app to use an external URL; -2. Make a clone of this repository; -3. Open the project folder in a terminal; -4. Run `yarn` to install dependencies; -5. Run `yarn start` to launch at `http://localhost:3000`. - ---- - -### Running from GitHub Pages - -1. You need to have the [Ollama server](https://ollama.com/download) installed on your machine, or configure the app to use an external URL; -2. By default, the app uses the llama3 model, you can install it with the command: `ollama run llama3`; -3. If you have the local server, run it with the following command releasing CORS: `export OLLAMA_ORIGINS=https://*.github.io && ollama serve`; -4. Access at: [kastorcode.github.io/ollama-gui-reactjs](https://kastorcode.github.io/ollama-gui-reactjs). - ---- - -

- <kastor.code/> -

\ No newline at end of file diff --git a/asset-manifest.json b/asset-manifest.json new file mode 100644 index 0000000..0678d89 --- /dev/null +++ b/asset-manifest.json @@ -0,0 +1,28 @@ +{ + "files": { + "main.css": "/ollama-gui-reactjs/static/css/main.e2628c28.css", + "main.js": "/ollama-gui-reactjs/static/js/main.b0fd8985.js", + "static/css/595.50dcd131.chunk.css": "/ollama-gui-reactjs/static/css/595.50dcd131.chunk.css", + "static/js/595.fdded3de.chunk.js": "/ollama-gui-reactjs/static/js/595.fdded3de.chunk.js", + "static/js/665.052c1bd6.chunk.js": "/ollama-gui-reactjs/static/js/665.052c1bd6.chunk.js", + "static/js/625.c20445b6.chunk.js": "/ollama-gui-reactjs/static/js/625.c20445b6.chunk.js", + "static/css/755.4e4fde9a.chunk.css": "/ollama-gui-reactjs/static/css/755.4e4fde9a.chunk.css", + "static/js/755.269bb141.chunk.js": "/ollama-gui-reactjs/static/js/755.269bb141.chunk.js", + "static/js/413.5110e15e.chunk.js": "/ollama-gui-reactjs/static/js/413.5110e15e.chunk.js", + "static/media/sf-pro-regular.otf": "/ollama-gui-reactjs/static/media/sf-pro-regular.d1f5e6e16dd4f75c3950.otf", + "index.html": "/ollama-gui-reactjs/index.html", + "main.e2628c28.css.map": "/ollama-gui-reactjs/static/css/main.e2628c28.css.map", + "main.b0fd8985.js.map": "/ollama-gui-reactjs/static/js/main.b0fd8985.js.map", + "595.50dcd131.chunk.css.map": "/ollama-gui-reactjs/static/css/595.50dcd131.chunk.css.map", + "595.fdded3de.chunk.js.map": "/ollama-gui-reactjs/static/js/595.fdded3de.chunk.js.map", + "665.052c1bd6.chunk.js.map": "/ollama-gui-reactjs/static/js/665.052c1bd6.chunk.js.map", + "625.c20445b6.chunk.js.map": "/ollama-gui-reactjs/static/js/625.c20445b6.chunk.js.map", + "755.4e4fde9a.chunk.css.map": "/ollama-gui-reactjs/static/css/755.4e4fde9a.chunk.css.map", + "755.269bb141.chunk.js.map": "/ollama-gui-reactjs/static/js/755.269bb141.chunk.js.map", + "413.5110e15e.chunk.js.map": "/ollama-gui-reactjs/static/js/413.5110e15e.chunk.js.map" + }, + "entrypoints": [ + "static/css/main.e2628c28.css", + "static/js/main.b0fd8985.js" + ] +} \ No newline at end of file diff --git a/craco.config.js b/craco.config.js deleted file mode 100644 index 0b8221f..0000000 --- a/craco.config.js +++ /dev/null @@ -1,9 +0,0 @@ -const path = require('path') - -module.exports = { - webpack: { - alias: { - '~': path.resolve(__dirname, 'src') - } - } -} \ No newline at end of file diff --git a/public/favicon.svg b/favicon.svg similarity index 100% rename from public/favicon.svg rename to favicon.svg diff --git a/index.html b/index.html new file mode 100644 index 0000000..3b95ae0 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ +Ollama GUI
\ No newline at end of file diff --git a/public/manifest.json b/manifest.json similarity index 100% rename from public/manifest.json rename to manifest.json diff --git a/package.json b/package.json deleted file mode 100644 index cf8a6ea..0000000 --- a/package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "ollama-gui-reactjs", - "version": "1.0.0", - "description": "Ollama Graphical User Interface", - "author": " Matheus Ramalho de Oliveira", - "private": false, - "homepage": "https://kastorcode.github.io/ollama-gui-reactjs", - "scripts": { - "start": "craco start", - "build": "craco build", - "test": "craco test", - "eject": "craco eject", - "predeploy": "yarn build", - "deploy": "gh-pages -d build" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "dependencies": { - "@testing-library/jest-dom": "^5.17.0", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^13.5.0", - "@types/jest": "^27.5.2", - "@types/node": "^16.18.98", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-hooks-global-state": "^2.1.0", - "react-router-dom": "^6.23.1", - "react-scripts": "5.0.1", - "react-transition-group": "^4.4.5", - "styled-components": "^6.1.11", - "typescript": "^4.9.5", - "web-vitals": "^2.1.4" - }, - "devDependencies": { - "@craco/craco": "^7.1.0", - "@types/react-transition-group": "^4.4.10", - "gh-pages": "^6.1.1" - } -} diff --git a/public/404.html b/public/404.html deleted file mode 100644 index 499ef7a..0000000 --- a/public/404.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/public/index.html b/public/index.html deleted file mode 100644 index edb8403..0000000 --- a/public/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - Ollama GUI - - - - - -
- - - \ No newline at end of file diff --git a/screenshots/0.png b/screenshots/0.png deleted file mode 100644 index a528211..0000000 Binary files a/screenshots/0.png and /dev/null differ diff --git a/screenshots/1.png b/screenshots/1.png deleted file mode 100644 index ea3263c..0000000 Binary files a/screenshots/1.png and /dev/null differ diff --git a/screenshots/2.png b/screenshots/2.png deleted file mode 100644 index 74a54a5..0000000 Binary files a/screenshots/2.png and /dev/null differ diff --git a/screenshots/3.png b/screenshots/3.png deleted file mode 100644 index a8b5a91..0000000 Binary files a/screenshots/3.png and /dev/null differ diff --git a/screenshots/4.png b/screenshots/4.png deleted file mode 100644 index feda86c..0000000 Binary files a/screenshots/4.png and /dev/null differ diff --git a/screenshots/5.png b/screenshots/5.png deleted file mode 100644 index fb79c00..0000000 Binary files a/screenshots/5.png and /dev/null differ diff --git a/src/assets/images/loading.png b/src/assets/images/loading.png deleted file mode 100644 index 39d0435..0000000 Binary files a/src/assets/images/loading.png and /dev/null differ diff --git a/src/components/about/index.tsx b/src/components/about/index.tsx deleted file mode 100644 index 58e6848..0000000 --- a/src/components/about/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { ColumnContainer, Filler } from '~/components/containers' - -export default function About () { - - return ( - -

This app is a front end for the LLM (large language model) Ollama.

-

Ollama is an interface created by Meta that facilitates the use of artificial intelligence.

-

The initial setup uses the Llama 3 model.

-
- - <kastor.code/> - -
- ) - -} \ No newline at end of file diff --git a/src/components/button/index.tsx b/src/components/button/index.tsx deleted file mode 100644 index c8d3f04..0000000 --- a/src/components/button/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react' - -import { StyledButton } from './style' - - -interface ButtonProps { - children : React.ReactNode, - onClick ?: Function, - style ?: React.CSSProperties -} - - -export default function Button ({ children, onClick, style } : ButtonProps) { - return ( - onClick && onClick()} - style={style} - > - { children } - - ) -} \ No newline at end of file diff --git a/src/components/button/style.ts b/src/components/button/style.ts deleted file mode 100644 index bbd704d..0000000 --- a/src/components/button/style.ts +++ /dev/null @@ -1,28 +0,0 @@ -import styled from 'styled-components' - -export const StyledButton = styled.button` - background-color: #007aff; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); - color: #ffffff; - cursor: pointer; - display: inline-block; - font-weight: 500; - padding: 12px 24px; - transition: background-color 0.3s, box-shadow 0.3s; - - &:hover { - background-color: #005bb5; - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15); - } - - &:focus { - box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.3); - outline: none; - } - - &:active { - background-color: #004a99; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); - } -` \ No newline at end of file diff --git a/src/components/containers/index.tsx b/src/components/containers/index.tsx deleted file mode 100644 index 3eaacef..0000000 --- a/src/components/containers/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { forwardRef } from 'react' - -import useIsMobile from '~/hooks/useIsMobile' -import { StyledColumnContainer, StyledRowContainer } from './style' - -interface ContainerProps { - children : React.ReactNode, - style ?: React.CSSProperties -} - -export function ColumnContainer ({ children, style } : ContainerProps) { - - const isMobile = useIsMobile() - - return ( - - { children } - - ) - -} - -export const RowContainer = forwardRef -(({ children, style } : ContainerProps, ref) => { - - const isMobile = useIsMobile() - - return ( - - { children } - - ) - -}) - -interface FillerProps { - children ?: React.ReactNode, - height ?: React.CSSProperties['height'], - textAlign ?: React.CSSProperties['textAlign'], - width ?: React.CSSProperties['width'] -} - -export function Filler ({ - children, height='100%', textAlign='center', width='100%' -} : FillerProps) { - - return ( -
- { children } -
- ) - -} \ No newline at end of file diff --git a/src/components/containers/style.ts b/src/components/containers/style.ts deleted file mode 100644 index b1e3887..0000000 --- a/src/components/containers/style.ts +++ /dev/null @@ -1,20 +0,0 @@ -import styled from 'styled-components' - -export const StyledColumnContainer = styled.div<{ isMobile : boolean }>` - align-items: center; - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - min-width: ${({ isMobile }) => isMobile ? '100vw' : '360px'}; - width: 100%; -` - -export const StyledRowContainer = styled.div<{ isMobile : boolean }>` - align-items: center; - display: flex; - flex-direction: row; - height: 100%; - overflow-x: ${({ isMobile }) => isMobile ? 'auto' : 'hidden'}; - width: 100%; -` \ No newline at end of file diff --git a/src/components/footer/index.tsx b/src/components/footer/index.tsx deleted file mode 100644 index 1845ebd..0000000 --- a/src/components/footer/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { StyledFooter } from './style' - -export default function Footer () { - - return ( - -  •  - kastorcode -  •  - ollama -  •  - - ) - -} \ No newline at end of file diff --git a/src/components/footer/style.ts b/src/components/footer/style.ts deleted file mode 100644 index 7e8d8d6..0000000 --- a/src/components/footer/style.ts +++ /dev/null @@ -1,10 +0,0 @@ -import styled from 'styled-components' - -export const StyledFooter = styled.footer` - align-items: center; - background-color: rgba(0,0,0,0.2); - display: flex; - justify-content: flex-end; - padding: 8px; - width: 100%; -` \ No newline at end of file diff --git a/src/components/menu/index.tsx b/src/components/menu/index.tsx deleted file mode 100644 index 91ea335..0000000 --- a/src/components/menu/index.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { useEffect, useRef } from 'react' -import { useNavigate } from 'react-router-dom' - -import ROUTES from '~/constants/routes' -import useIsMobile from '~/hooks/useIsMobile' -import getChatIndex from '~/services/getChatIndex' -import saveChat from '~/services/saveChat' -import scroller from '~/services/scroller' -import Store from '~/services/store' -import { disChats, useChats } from '~/stores/chats' -import { deleteChat } from '~/stores/chats/actions' -import Button from '~/components/button' -import { ButtonsContainer, Chat, ChatsContainer, Container } from './style' - -interface MenuProps { - loading ?: boolean, - scrollRef : React.RefObject -} - -export default function Menu ({ loading, scrollRef } : MenuProps) { - - const navigate = useNavigate() - const isMobile = useIsMobile() - const chats = useChats('chats') - const deletedChatRef = useRef(false) - - function sharedValidations () { - if (loading) return null - const chatIndex = getChatIndex() - if (!Array.isArray(chats[chatIndex])) return null - return chatIndex - } - - function handleDeleteChat () { - const chatIndex = sharedValidations() - if (chatIndex === null) return - const confirmed = confirm('Are you sure you want to delete?') - if (!confirmed) return - deletedChatRef.current = true - disChats(deleteChat(chatIndex)) - } - - function handleSaveChat () { - const chatIndex = sharedValidations() - if (chatIndex === null) return - saveChat(chatIndex, chats[chatIndex]) - } - - useEffect(() => { - if (!deletedChatRef.current) return - deletedChatRef.current = false - navigate(ROUTES.ROOT) - Store.set('chats', chats) - }, [chats]) - - return ( - - - { chats.map((messages, index) => { - const { content } = messages[0] - return ( - - { content } - - ) - }) } - { chats.length > 0 && ( - + - )} - - - - - - { isMobile && ( - - )} - - - ) -} \ No newline at end of file diff --git a/src/components/menu/style.ts b/src/components/menu/style.ts deleted file mode 100644 index 0461d70..0000000 --- a/src/components/menu/style.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Link } from 'react-router-dom' -import styled from 'styled-components' - -export const ButtonsContainer = styled.div` - display: flex; - height: fit-content; - justify-content: space-between; - padding: 8px; - width: 100%; -` - -export const Chat = styled(Link)` - border-radius: 4px; - color: rgba(255,255,255,0.25); - cursor: pointer; - display: block; - margin-bottom: 4px; - overflow: hidden; - padding: 4px; - text-overflow: ellipsis; - transition: 0.3s; - white-space: nowrap; - width: 100%; - - &:hover { - background-color: rgba(255,255,255,0.25); - box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.2); - color: #fff; - } -` - -export const ChatsContainer = styled.div` - height: 100%; - overflow-y: auto; - padding: 8px; - width: 100%; -` - -export const Container = styled.div<{ isMobile : boolean }>` - background-color: rgba(0,0,0,0.2); - display: flex; - flex-direction: column; - height: 100%; - min-width: ${({ isMobile }) => isMobile ? '100vw' : '20%'}; -` \ No newline at end of file diff --git a/src/components/textInput/index.tsx b/src/components/textInput/index.tsx deleted file mode 100644 index f208b9e..0000000 --- a/src/components/textInput/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React, { forwardRef } from 'react' - -import { Input } from './style' - - -interface InputProps { - onChange : (e : React.ChangeEvent) => void, - placeholder : string, - value : string -} - - -export default forwardRef((props, ref) => { - return ( - - ) -}) \ No newline at end of file diff --git a/src/components/textInput/style.ts b/src/components/textInput/style.ts deleted file mode 100644 index 16a8310..0000000 --- a/src/components/textInput/style.ts +++ /dev/null @@ -1,21 +0,0 @@ -import styled from 'styled-components' - -export const Input = styled.input` - background-color: rgba(255,255,255,0.25); - border: 1px solid #dcdcdc; - border-radius: 8px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - color: #fff; - margin-right: 8px; - outline: none; - overflow-y: hidden; - padding: 12px 16px; - resize: none; - transition: border-color 0.3s, box-shadow 0.3s; - width: 100%; - - &:focus { - border-color: #007aff; - box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.2); - } -` \ No newline at end of file diff --git a/src/components/textarea/index.tsx b/src/components/textarea/index.tsx deleted file mode 100644 index 462aeaf..0000000 --- a/src/components/textarea/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, { forwardRef } from 'react' - -import { StyledTextArea } from './style' - - -interface TextAreaProps { - placeholder : string -} - - -export default forwardRef((props, ref) => { - return ( - - ) -}) \ No newline at end of file diff --git a/src/components/textarea/style.ts b/src/components/textarea/style.ts deleted file mode 100644 index c3cb876..0000000 --- a/src/components/textarea/style.ts +++ /dev/null @@ -1,21 +0,0 @@ -import styled from 'styled-components' - -export const StyledTextArea = styled.textarea` - background-color: rgba(255,255,255,0.25); - border: 1px solid #dcdcdc; - border-radius: 8px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - color: #fff; - margin-right: 8px; - outline: none; - overflow-y: hidden; - padding: 12px 16px; - resize: none; - transition: border-color 0.3s, box-shadow 0.3s; - width: 100%; - - &:focus { - border-color: #007aff; - box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.2); - } -` \ No newline at end of file diff --git a/src/components/toggle/index.tsx b/src/components/toggle/index.tsx deleted file mode 100644 index b3124f7..0000000 --- a/src/components/toggle/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' - -import { Ball, Checkbox, Container, Label, ToggleContainer } from './style' - -interface ToggleProps { - checked : boolean - id : string - label : string - onChange : () => any -} - - -export default function Toggle ({ checked, id, label, onChange } : ToggleProps) { - return ( - - { label } - - - - - - ) -} \ No newline at end of file diff --git a/src/components/toggle/style.ts b/src/components/toggle/style.ts deleted file mode 100644 index fe831bf..0000000 --- a/src/components/toggle/style.ts +++ /dev/null @@ -1,55 +0,0 @@ -import styled from 'styled-components' - -export const Ball = styled.span<{ checked : boolean }>` - background-color: white; - border-radius: 50%; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - height: 26px; - left: 4px; - position: absolute; - top: 4px; - transform: translateX(${({ checked }) => (checked ? '26px' : '0')}); - transition: transform 0.3s ease; - width: 26px; -` - -export const Checkbox = styled.input` - display: none; -` - -export const Container = styled.div` - align-items: center; - display: flex; - justify-content: space-between; - width: 100%; -` - -export const Label = styled.label<{ checked : boolean }>` - background-color: ${({ checked }) => (checked ? '#007aff' : '#ccc')}; - border-radius: 34px; - cursor: pointer; - display: block; - height: 100%; - position: relative; - transition: background-color 0.3s ease; - width: 100%; - - &:before { - background-color: #f0f0f5; - border-radius: inherit; - content: ''; - height: 100%; - left: 50%; - position: absolute; - top: 50%; - transform: translate(-50%, -50%) scale(${({ checked }) => (checked ? 0 : 1.1)}); - transition: transform 0.3s ease; - width: 100%; - } -` - -export const ToggleContainer = styled.div` - height: 34px; - position: relative; - width: 60px; -` \ No newline at end of file diff --git a/src/constants/routes.ts b/src/constants/routes.ts deleted file mode 100644 index 7533712..0000000 --- a/src/constants/routes.ts +++ /dev/null @@ -1,9 +0,0 @@ -const ROOT = '/ollama-gui-reactjs' - -export default { - ROOT, - CHAT: `${ROOT}/chat`, - CHAT_INDEX: `${ROOT}/chat/:chat`, - CONFIG: `${ROOT}/config`, - GO_BACK: -1 -} \ No newline at end of file diff --git a/src/declarations.d.ts b/src/declarations.d.ts deleted file mode 100644 index 261b7d2..0000000 --- a/src/declarations.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module '*.png' \ No newline at end of file diff --git a/src/entities/messages.ts b/src/entities/messages.ts deleted file mode 100644 index afbb6fa..0000000 --- a/src/entities/messages.ts +++ /dev/null @@ -1,31 +0,0 @@ -export interface Message { - role : 'assistant' | 'user', - content : string, - time : number -} - -export type Messages = Message[] - -export type Chats = Message[][] - -interface ModelDefaultResponse { - created_at : string, - done : boolean, - message : Message, - model : string -} - -interface ModelFinalResponse { - created_at : string, - done : boolean, - done_reason : string, - eval_count : number, - eval_duration : number, - load_duration : number, - message : Message, - model : string, - prompt_eval_duration : number, - total_duration : number -} - -export type ModelResponse = ModelDefaultResponse | ModelFinalResponse \ No newline at end of file diff --git a/src/entities/storeAction.ts b/src/entities/storeAction.ts deleted file mode 100644 index 984ee75..0000000 --- a/src/entities/storeAction.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface StoreAction { - type : string - payload ?: any -} \ No newline at end of file diff --git a/src/hooks/useIsMobile.ts b/src/hooks/useIsMobile.ts deleted file mode 100644 index f936d54..0000000 --- a/src/hooks/useIsMobile.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useEffect, useState } from 'react' - -export default function useIsMobile (breakpoint = 768) { - - const [isMobile, setIsMobile] = useState(window.innerWidth <= breakpoint) - - useEffect(() => { - const handleResize = () => { - setIsMobile(window.innerWidth <= breakpoint) - } - window.addEventListener('resize', handleResize) - return () => { - window.removeEventListener('resize', handleResize) - } - }, [breakpoint]) - - return isMobile - -} \ No newline at end of file diff --git a/src/index.css b/src/index.css deleted file mode 100644 index fbc8c72..0000000 --- a/src/index.css +++ /dev/null @@ -1,70 +0,0 @@ -@font-face { - font-family: 'SF Pro'; - src: url('./assets/fonts/sf-pro-regular.otf') format('opentype'); -} - -* { - border: none; - box-sizing: border-box; - font-family: 'SF Pro', sans-serif; - font-size: 16px; - margin: 0; - padding: 0; - text-decoration: none; -} - -a { - color: #007aff -} - -a:hover { - color: #005bb5; -} - -a:active { - color: #004a99; -} - -body { - align-items: center; - background-color: #1C1C1E; - color: #F8F8F8; - display: flex; - height: 100svh; - justify-content: center; - overflow: hidden; - width: 100%; -} - -#root { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - overflow: hidden; - width: 100%; -} - -::-webkit-scrollbar { - height: 12px; - width: 12px; -} - -::-webkit-scrollbar-track { - background: #f1f1f1; - border-radius: 6px; -} - -::-webkit-scrollbar-thumb { - background: rgba(0, 0, 0, 0.3); - border: 3px solid #f1f1f1; - border-radius: 6px; -} - -::-webkit-scrollbar-thumb:hover { - background: rgba(0, 0, 0, 0.5); -} - -::-webkit-scrollbar-thumb:active { - background: rgba(0, 0, 0, 0.7); -} \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index ede4622..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import { RouterProvider } from 'react-router-dom' - -import '~/index.css' -import router from '~/router' - -const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement -) - -root.render( - - - -) \ No newline at end of file diff --git a/src/pages/chat/index.css b/src/pages/chat/index.css deleted file mode 100644 index d24f6d3..0000000 --- a/src/pages/chat/index.css +++ /dev/null @@ -1,6 +0,0 @@ -.userMessage { - background-color: rgba(255,255,255,0.25); - border-radius: 8px; - padding: 8px; - width: fit-content; -} \ No newline at end of file diff --git a/src/pages/chat/index.tsx b/src/pages/chat/index.tsx deleted file mode 100644 index 47a80a2..0000000 --- a/src/pages/chat/index.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { useEffect, useRef, useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' - -import ROUTES from '~/constants/routes' -import { Message, ModelResponse } from '~/entities/messages' -import createAssistantMessage from '~/services/createAssistantMessage' -import createUserMessage from '~/services/createUserMessage' -import getChatIndex from '~/services/getChatIndex' -import requester from '~/services/requester' -import scroller from '~/services/scroller' -import Store from '~/services/store' -import { disChats, useChats } from '~/stores/chats' -import { addMessage } from '~/stores/chats/actions' -import { useConfig } from '~/stores/config' -import About from '~/components/about' -import Button from '~/components/button' -import { ColumnContainer, RowContainer } from '~/components/containers' -import Menu from '~/components/menu' -import TextArea from '~/components/textarea' -import { InputContainer, Loading, Talk } from './style' -import LOADING from '~/assets/images/loading.png' -import './index.css' - - -export default function Chat () { - - const navigate = useNavigate() - const { chat } = useParams() - const [index, setIndex] = useState(null) - const [loading, setLoading] = useState(true) - const chats = useChats('chats') - const { autoSaveChats, modelName, modelUrl } = useConfig('config') - const assistantMessage = useRef(createAssistantMessage('')) - const renderCount = useRef(0) - const rowContainerRef = useRef(null) - const talkRef = useRef(null) - const textAreaRef = useRef(null) - - function requestHandler () { - if (loading) return - const messageText = getMessageText() - if (!messageText) return - setLoading(true) - const userMessage = createUserMessage(messageText) - updateTalk(false, userMessage) - disChats(addMessage({ index: index as number, message: userMessage })) - assistantMessage.current = createAssistantMessage('') - requester( - modelUrl, modelName, chats[index as number], responseHandler, errorHandler - ) - } - - function responseHandler (response : ModelResponse) { - if (response.message.content) { - updateTalk(false, response.message) - assistantMessage.current.content += response.message.content - } - if (response.done) { - appendBr(2) - disChats(addMessage({ index: index as number, message: assistantMessage.current })) - setLoading(false) - } - } - - function errorHandler (error : unknown) { - alert('Something went wrong :-(') - console.error(error) - setLoading(false) - } - - function getMessageText () { - return textAreaRef.current?.value as string - } - - function updateTalk (loaded : boolean, { role, content } : Message) { - roles[role](content) - if (loaded && role === 'assistant') { - appendBr(2) - } - } - - const roles : { - [key in Message['role']] : (content : string) => void - } = { - assistant: content => { - const span = document.createElement('span') - span.innerText = content - talkRef.current?.appendChild(span) - }, - user: content => { - clearTextArea() - const p = document.createElement('p') - p.classList.add('userMessage') - p.innerText = content - talkRef.current?.appendChild(p) - appendBr() - } - } - - function clearTextArea () { - (textAreaRef.current as HTMLTextAreaElement).value = '' - textAreaRef.current?.focus() - } - - function appendBr (amount = 1) { - for (let i = 0; i < amount; i++) { - talkRef.current?.appendChild(document.createElement('br')) - } - } - - useEffect(() => { - renderCount.current++ - if (!autoSaveChats) return - if (renderCount.current < 3) return - Store.set('chats', chats) - }, [autoSaveChats, chats]) - - useEffect(() => { - if (index === null) { - return - } - if (index < 0 || index > chats.length) { - navigate(ROUTES.ROOT) - return - } - scroller(rowContainerRef, 1) - if (chats[index]) { - if (!loading) setLoading(true) - chats[index].forEach(message => { - updateTalk(true, message) - }) - } - setLoading(false) - }, [index]) - - useEffect(() => { - textAreaRef.current?.focus() - talkRef.current?.replaceChildren() - setIndex(getChatIndex()) - }, [chat]) - - return ( - - - - { loading && } - { chats.length ? : } - -