From 5e1e0df0ff5883682849ef71ead3e59647a5e6ea Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 6 Nov 2023 18:23:34 +0100 Subject: [PATCH 01/13] feat: playground and huggingface local and remote endpoints --- .gitignore | 5 + README.md | 124 ++++--- bun.lockb | Bin 250789 -> 249679 bytes package.json | 12 +- src/app/AgentConversationAccordionItem.tsx | 22 ++ src/app/AgentPromptTemplate.tsx | 18 + src/app/AgentPromptTemplateAccordionItem.tsx | 22 ++ src/app/AgentView.tsx | 190 ++++++++++ src/app/App.tsx | 17 - src/app/AppAccordionItem.tsx | 21 ++ src/app/AppView.tsx | 14 + src/app/AttachmentsAccordionItem.tsx | 63 ++++ src/app/AutoTextarea.tsx | 40 --- src/app/ChatConversation.tsx | 66 ++-- src/app/ChatConversationMessage.tsx | 8 +- src/app/ChatSettings.tsx | 325 ------------------ src/app/ChatSettingsModelSelector.tsx | 49 --- src/app/ChatSettingsSlider.tsx | 24 -- src/app/ChatView.tsx | 17 + src/app/ChatWithSettings.tsx | 12 - src/app/CopyOverlay.tsx | 12 - src/app/PlaygroundView.tsx | 63 ++++ src/app/PresetSelector.tsx | 41 --- src/app/Router.tsx | 30 ++ src/app/Sidebar.tsx | 87 +---- src/app/SidebarAgentList.tsx | 52 +++ src/app/SidebarChatList.tsx | 81 ++--- src/components/ActionOverlay.tsx | 26 ++ src/components/AgentConversation.tsx | 17 + src/components/AgentGenerationParams.tsx | 84 +++++ .../AgentGenerationParamsAccordionItem.tsx | 16 + src/components/AgentMessage.tsx | 41 +++ src/components/AgentPicker.tsx | 42 +++ src/components/AutoTextarea.tsx | 40 +++ src/components/ChatSettings.tsx | 198 +++++++++++ src/components/Editor.tsx | 34 ++ src/components/ModelPicker.tsx | 29 ++ src/components/ModelSelect.tsx | 38 ++ src/components/Picker.tsx | 42 +++ src/components/PresetPicker.tsx | 55 +++ src/components/Select.tsx | 47 +++ src/components/Sheet.tsx | 5 + src/components/SidebarItem.tsx | 38 ++ src/components/SidebarSection.tsx | 16 + src/components/SliderCheckbox.tsx | 42 +++ src/{app => components}/ToggleDarkButton.tsx | 6 +- src/components/VariablesAccordionItem.tsx | 42 +++ src/components/ui/button.tsx | 5 +- src/components/ui/input.tsx | 9 +- src/components/ui/select.tsx | 8 +- src/index.tsx | 33 +- src/lib/OllamaClient.ts | 85 ----- src/lib/adapters/HuggingFace.ts | 56 +++ src/lib/adapters/Ollama.ts | 129 +++++++ src/lib/adapters/index.ts | 12 + src/lib/extraction.ts | 63 ++-- src/lib/utils.ts | 19 - src/lib/utils.tsx | 69 ++++ src/store/AdapterFactory.ts | 43 +++ src/store/Agent.ts | 202 +++++++++++ src/store/Attachment.ts | 37 ++ src/store/Chat.ts | 232 +++++++++++++ src/store/Chat.tsx | 209 ----------- src/store/{Message.tsx => Message.ts} | 13 +- src/store/{Model.tsx => Model.ts} | 0 src/store/Playground.ts | 43 +++ src/store/Presets.tsx | 85 ----- src/store/State.ts | 26 ++ src/store/Store.ts | 45 +++ src/store/Store.tsx | 72 ---- src/store/Template.ts | 13 + src/store/defaults.ts | 37 ++ src/store/{index.tsx => index.ts} | 0 73 files changed, 2570 insertions(+), 1248 deletions(-) create mode 100644 src/app/AgentConversationAccordionItem.tsx create mode 100644 src/app/AgentPromptTemplate.tsx create mode 100644 src/app/AgentPromptTemplateAccordionItem.tsx create mode 100644 src/app/AgentView.tsx delete mode 100644 src/app/App.tsx create mode 100644 src/app/AppAccordionItem.tsx create mode 100644 src/app/AppView.tsx create mode 100644 src/app/AttachmentsAccordionItem.tsx delete mode 100644 src/app/AutoTextarea.tsx delete mode 100644 src/app/ChatSettings.tsx delete mode 100644 src/app/ChatSettingsModelSelector.tsx delete mode 100644 src/app/ChatSettingsSlider.tsx create mode 100644 src/app/ChatView.tsx delete mode 100644 src/app/ChatWithSettings.tsx delete mode 100644 src/app/CopyOverlay.tsx create mode 100644 src/app/PlaygroundView.tsx delete mode 100644 src/app/PresetSelector.tsx create mode 100644 src/app/Router.tsx create mode 100644 src/app/SidebarAgentList.tsx create mode 100644 src/components/ActionOverlay.tsx create mode 100644 src/components/AgentConversation.tsx create mode 100644 src/components/AgentGenerationParams.tsx create mode 100644 src/components/AgentGenerationParamsAccordionItem.tsx create mode 100644 src/components/AgentMessage.tsx create mode 100644 src/components/AgentPicker.tsx create mode 100644 src/components/AutoTextarea.tsx create mode 100644 src/components/ChatSettings.tsx create mode 100644 src/components/Editor.tsx create mode 100644 src/components/ModelPicker.tsx create mode 100644 src/components/ModelSelect.tsx create mode 100644 src/components/Picker.tsx create mode 100644 src/components/PresetPicker.tsx create mode 100644 src/components/Select.tsx create mode 100644 src/components/Sheet.tsx create mode 100644 src/components/SidebarItem.tsx create mode 100644 src/components/SidebarSection.tsx create mode 100644 src/components/SliderCheckbox.tsx rename src/{app => components}/ToggleDarkButton.tsx (76%) create mode 100644 src/components/VariablesAccordionItem.tsx delete mode 100644 src/lib/OllamaClient.ts create mode 100644 src/lib/adapters/HuggingFace.ts create mode 100644 src/lib/adapters/Ollama.ts create mode 100644 src/lib/adapters/index.ts delete mode 100644 src/lib/utils.ts create mode 100644 src/lib/utils.tsx create mode 100644 src/store/AdapterFactory.ts create mode 100644 src/store/Agent.ts create mode 100644 src/store/Attachment.ts create mode 100644 src/store/Chat.ts delete mode 100644 src/store/Chat.tsx rename src/store/{Message.tsx => Message.ts} (57%) rename src/store/{Model.tsx => Model.ts} (100%) create mode 100644 src/store/Playground.ts delete mode 100644 src/store/Presets.tsx create mode 100644 src/store/State.ts create mode 100644 src/store/Store.ts delete mode 100644 src/store/Store.tsx create mode 100644 src/store/Template.ts create mode 100644 src/store/defaults.ts rename src/store/{index.tsx => index.ts} (100%) diff --git a/.gitignore b/.gitignore index ab5afb2..31ef12d 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,8 @@ dist # Finder (MacOS) folder config .DS_Store +.aider* + + +models/ +``` diff --git a/README.md b/README.md index 1bb8c81..dd6ae82 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,93 @@ # LLM Workbench -A one-stop-shop for all your LLM needs. Unleash the power of FOSS language models on your local machine. +LLM Workbench is a user-friendly web interface designed for large language models, built with React and MobX, styled using Shadcn UI. It serves as a one-stop solution for all your large language model needs, enabling you to harness the power of free, open-source language models on your local machine. -# Usage +### Getting Started + +To start your journey, choose between a HuggingFace Text Inference Generation Endpoint or Ollama. + +#### HuggingFace Text Inference Generation Endpoint ```bash -OLLAMA_ORIGINS="https://knoopx.github.io" ollama serve +docker run --gpus all --shm-size 1g -p 8080:80 -v (pwd)/models:/data ghcr.io/huggingface/text-generation-inference:1.1.0 --trust-remote-code --model-id TheBloke/deepseek-coder-33B-instruct-AWQ --quantize awq ``` -or add to `/etc/systemd/system/ollama.service`: +#### Ollama +```bash +OLLAMA_ORIGINS="https://knoopx.github.io" ollama serve ``` + +or add this line to `/etc/systemd/system/ollama.service`: + +```bash Environment=OLLAMA_ORIGINS="https://knoopx.github.io" ``` -and restart Ollama: +Restart Ollama using these commands: -``` +```bash systemctl daemon-reload systemctl restart ollama ``` -# Features - -- UI - - Simple, clean interface - - Dark mode -- LLM API Client - - Ollama -- Chat Interface - - Output Streaming - - Regenerate/Continue/Undo/Clear - - Markdown Rendering -- Complete generation control - - System prompts - - Conversation History - - Chat prompt template +## 🎭 Features + +### 💬 Chat Interface + +- **Simple, clean interface**: We've designed a user-friendly interface that makes it easy for you to interact with the AI model. +- **Output streaming**: See the generated text in real-time as you type your prompt. +- **Regenerate/Continue/Undo/Clear**: Use these buttons to control the generation process. +- **Markdown Rendering**: The AI will generate text that supports Markdown formatting, making it easy for you to create styled content. +- **Generation canceling**: Stop the generation process at any time by clicking the "Cancel" button. +- **Dark mode**: Prefer working in the dark? Toggle on Dark mode for a more comfortable experience. +- **Attachments**: Attach files to your chat messages (pdf, docx, and plain-text supported only). + +### 🛹 Playground + +- **Copilot-alike inline completion**: Type your prompt and let the AI suggest completions as you type. +- **Tab to accept**: Press the Tab key to accept the suggested completion. +- **Cltr+Enter to re-generate**: Hit Ctrl+Enter to re-generate the response with the same prompt. + +### 🤖 Agents + +- **Connection Adapters**: We support various connection adapters, including Ollama and HuggingFace TGI (local or remote). +- **Complete generation control**: Customize the agent behavior with system prompts, conversation history, and chat prompt templates. # Future Ideas -- window.ai integration -- Copy to clipboard overlay over messages -- Full page prompt editor -- Collapsible side panels -- Cancel generation -- Import/Export chats -- Some kind of Agents -- One-shot Tasks/Apps -- Tools -- LLM Connection adapters -- Prompt management -- Model management -- RAG/Embeddings/Vector Search - - https://github.com/jacoblee93/fully-local-pdf-chatbot - - https://github.com/tantaraio/voy - - https://do-me.github.io/SemanticFinder/ - - https://github.com/yusufhilmi/client-vector-search - - https://github.com/mwilliamson/mammoth.js -- Other pipelines - - TTS - - Re-formating - - https://huggingface.co/ldenoue/distilbert-base-re-punctuate - - Summarization - - https://huggingface.co/ldenoue/distilbart-cnn-6-6 - - Translation - - Speech Recognition - - https://huggingface.co/docs/transformers.js/api/pipelines#pipelinesautomaticspeechrecognitionpipeline - - NER - - https://huggingface.co/Xenova/bert-base-NER - - https://winkjs.org/wink-nlp/wink-nlp-in-browsers.html +- Import/Export chats - Importing and exporting chat data for convenience. +- Token Counter - A feature to count tokens in text. +- Copy fenced block to clipboard - The ability to copy a code block and paste it into the clipboard. +- Collapsible side panels - Side panels that can be expanded or collapsed for better organization. +- [window.ai](https://windowai.io/) integration + +Code Interpreters: + +- Hugging Face agents ([@huggingface/agents](https://github.com/huggingface/agents)) +- Aider ([paul-gauthier/aider](https://github.com/paul-gauthier/aider)) +- Functionary ([MeetKai/functionary](https://github.com/MeetKai/functionary)) +- NexusRaven model ([Nexusflow/NexusRaven-13B](https://huggingface.co/Nexusflow/NexusRaven-13B)) +- Open procedures database ([KillianLucas/open-procedures](https://raw.githubusercontent.com/KillianLucas/open-procedures/main/procedures_db.json)) +- ReACT ([www.promptingguide.ai/techniques/react](https://www.promptingguide.ai/techniques/react)) + +Model management features: + +- Hugging Face Hub ([@huggingface/hub](https://huggingface.co/docs/huggingface.js/hub/modules)) +- GPT4All catalog ([nomic-ai/gpt4all](https://raw.githubusercontent.com/nomic-ai/gpt4all/main/gpt4all-chat/metadata/models2.json)) +- LM Studio catalog [lmstudio-ai/model-catalog](https://raw.githubusercontent.com/lmstudio-ai/model-catalog/main/catalog.json) + +RAG, embeddings and vector search: + +- Client vector search ([yusufhilmi/client-vector-search](https://github.com/yusufhilmi/client-vector-search)) - in-browser vector database. +- Fully local PDF chatbot ([jacoblee93/fully-local-pdf-chatbot](https://github.com/jacoblee93/fully-local-pdf-chatbot)) - related. +- SemanticFinder ([do-me.github.io/SemanticFinder](https://do-me.github.io/SemanticFinder/)) - related. + +Other potential pipelines to consider: + +- TTS (Text-to-Speech) - convert text into speech. +- Reformatting - such as punctuation and re-punctuation models ([ldenoue/distilbert-base-re-punctuate](https://huggingface.co/ldenoue/distilbert-base-re-punctuate)). +- Summarization - summarize long text into shorter versions ([ldenoue/distilbart-cnn-6-6](https://huggingface.co/ldenoue/distilbart-cnn-6-6)). +- Translation - convert text between languages. +- Automatic speech recognition pipeline ([transformers.js](https://huggingface.co/docs/transformers.js/api/pipelines#pipelinesautomaticspeechrecognitionpipeline)) - convert spoken words into written text. +- Named Entity Recognition (NER) - identify and classify entities in text ([Xenova/bert-base-NER](https://huggingface.co/Xenova/bert-base-NER), [wink-nlp](https://winkjs.org/wink-nlp/wink-nlp-in-browsers.html)). diff --git a/bun.lockb b/bun.lockb index ee7fb0d7b357bfeda1ad3f60012270f715fd4200..d75fdf3e5a38a1d04c2dfc5cf0b95801510f4719 100755 GIT binary patch delta 50625 zcmeFacU)CR*9Cm<5tOT9FCd^|2L%BO5cHzPj##5$ji4wODI!=<@nV-KwxbUAu3$8l zSYuCOqE9rMW=Uc+%?O4>lNiO8Z>@9Y0P)ql&-?wp|Gu1`>+acW_UxJ2Q_kGuxw>_G z#f$4IF81|m*suEj!cJR{&-=1P)REc8XEsWGdgtQ{8-`41H?o1-qG``geq74LD{M(b zom%TVDw<+4MJ1;B#K$L?CMS*5m?hbD?ROnbCTEi=;}&FD$i%3`Bpb40QliJ(;zydc zIhjmVz^jl|A?=XWAQL1%Q0kp!eoM*Mm3&#q8nFMfj9%^rWKHPjAi>)+4kAIvo20>F zNLDyi>Z2g(pdTddtdP|qy=8tinQxN%UI&w@Jo3lIM2$#AH!!s^Ha(Lc_Wb18-u`%OpsVTO^QSr9Xv8mCpN4@rpA5Z}c zUVvnrkJU1nTp>3?xrt1T@~{4xM&|UZ|fl*AWEAcoZDa za64u>dtBB>p9^=PWB4t z^D$#dE0d`%q-$$^j~EUeRb{L}KI_f2Bf*x>gad|f2_!?-G00@Xw8#j7J6EM1~P5z0B+QDSPe9Nc}$%@U8^#65xlL_-O z;}~RJuK#67V18w!pdcf#v6F6?1<4SN?X2_3(A}XAf@B3DkaZxx>7qv>qN{E{VnovT z#8i{16B@_{HSeZ3bP*&Y`6(nL7n6__XN$MnqGwBk@sO4Dj8xl*q{LQmKz(wUKKlpt z(8ur+Btv@`QX48r4v8Nm8(bTb_k#sJ^@__&{UQ7?{|+Q4@zq}F80KHb*r;(SF_;)J ziK*BDVp7f^qbm3>U`WS%pmU7h=%YKl1j&lMWDhg?>h>N|5ACNn_$^45TLsCAJt46P zW}JIXZ=f^m?d)NE(nM`f-ripinF$$eVfp~QqSlb?(I7}p%C1tkMCy_FJwk8jZ>991 zLZw>hvDK!gjg2v757Z+$7qT+SheFbSvw>LuRgtI$f;|c!tS_rgkmP@Y!*Y;UA-UWx zLUIhR4bkOE=J2C}OfP=``JALV&{=*&RN{oF6qBj*>pH*wb<7~P zWB~|P*k-uy0EY_n!DI?h+gk!%Cq(JZ!2UQfah%DN5|c0i+nA}hy2Mh)-gl(l+tE?u zN5`OyD?V6hDai7W6{TKfl+K@o&I!C9lAU{q@{|QqhCnj^CM4SyHFjK5G+NLUIy*ZX z`wKhsB_tM1Kt?m0?qDhiMkg_8Y$_Wc6&*b;W`fCNMPW8*P@FC|s8MC>4DtcX!T4c} z&eoHiTq|RpkQ6=NycVA0HEK8#gi*I(giS zwDl8*VbwYbz5@4^bhM8S<88^~Z6o7Sj4eD?_iGv%GZG8VWEzn$5)m+&#*Wv+-OH>` zX8(!$l=u;n!;_j4Z;OVVFQS49GQI%M;W!1!RW>e7Z;(xsI9KE%qb4#MLDGQhOJZeo%0Cv8ql{xG6D-Axq*#^WVv3Do{-0;nN0N{ zEzqmlkqAP94HyxhqK#w>B0|S~Ah{px&d__{G+lR`f*U(V!E_ipBbORA&Sw-?%M3lV zDXBOZBup}$m-+%o4ncIwD3oFZ6Ik4|V{mG{AEO-g+mLMF6-X|<52Q?m

@fk^{6FvMP_JnMiQRH=qIR(Q-&u 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 { + const store = useStore(); + const { + state: { resource: agent }, + } = store; + + return ( + + + + ); +}); diff --git a/src/app/AgentPromptTemplate.tsx b/src/app/AgentPromptTemplate.tsx new file mode 100644 index 0000000..a86c0dd --- /dev/null +++ b/src/app/AgentPromptTemplate.tsx @@ -0,0 +1,18 @@ +import { Agent } from "@/store/Agent" +import { Instance } from "mobx-state-tree" +import { observer } from "mobx-react" +import { AutoTextarea } from "@/components/AutoTextarea" + +export const AgentPromptTemplate: React.FC<{ + agent: Instance +}> = observer(({ agent }) => { + return ( + + agent.update({ promptTemplate: prompt_template }) + } + /> + ) +}) diff --git a/src/app/AgentPromptTemplateAccordionItem.tsx b/src/app/AgentPromptTemplateAccordionItem.tsx new file mode 100644 index 0000000..98037cd --- /dev/null +++ b/src/app/AgentPromptTemplateAccordionItem.tsx @@ -0,0 +1,22 @@ +import { observer } from "mobx-react"; +import { useStore } from "@/store"; +import { TbPrompt } from "react-icons/tb"; +import { AppAccordionItem } from "./AppAccordionItem"; +import { AgentPromptTemplate } from "./AgentPromptTemplate"; + +const AgentPromptTemplateAccordionItem = observer(() => { + const store = useStore(); + const { + state: { resource: agent }, + } = store; + + return ( + + + + ); +}); diff --git a/src/app/AgentView.tsx b/src/app/AgentView.tsx new file mode 100644 index 0000000..11446f4 --- /dev/null +++ b/src/app/AgentView.tsx @@ -0,0 +1,190 @@ +import { observer } from "mobx-react" +import { ModelPicker } from "../components/ModelPicker" +import { useStore } from "@/store" +import { Input } from "@/components/ui/input" +import { AgentGenerationParams } from "../components/AgentGenerationParams" +import { AgentHistory } from "../components/AgentConversation" +import { AgentPromptTemplate } from "./AgentPromptTemplate" +import { Button } from "@/components/ui/button" +import { IoMdAdd, IoMdTrash } from "react-icons/io" +import { AutoTextarea } from "@/components/AutoTextarea" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { Picker } from "@/components/Picker" +import { Instance } from "mobx-state-tree" +import { Agent } from "@/store/Agent" +import { AdapterList } from "@/lib/adapters" +import { cn } from "@/lib/utils" + +const AgentConversation = observer(({ agent }) => ( +

+
+
+ Conversation History + +
+ + +
+ +
+
+ Templates + + + + + + +
{ + e.preventDefault() + document.dispatchEvent( + new KeyboardEvent("keydown", { key: "Escape" }), + ) + agent.addTemplate({ id: e.target.elements.id.value }) + }} + > + +
+
+
+
+ +
+
+ {agent.templates.map((template) => ( +
+
+
{template.id}
+ +
+ + { + template.update({ content }) + }} + /> +
+ ))} +
+
+
+ +
+ Prompt Template + + + Preview +
+ {agent.promptPreview} +
+
+
+
+)) + +const AdapterPicker: React.FC<{ + agent: Instance +}> = observer(({ agent }) => { + const options = AdapterList.map((adapter) => ({ + key: adapter, + value: adapter, + })) + + return ( +
+ Adapter + { + agent.adapter.update({ type }) + if (agent.adapter.baseUrl == "") { + if (agent.adapter.type == "Ollama") { + agent.adapter.update({ baseUrl: "http://localhost:11434" }) + } else if (agent.adapter.type == "HuggingFace") { + agent.adapter.update({ baseUrl: "http://localhost:8080" }) + } + } + }} + /> +
+ ) +}) + +export const AgentAdapterPicker: React.FC<{ + className?: string + agent: Instance +}> = observer(({ className, agent }) => ( +
+ + +
+ Endpoint + agent.adapter.update({ baseUrl: e.target.value })} + /> +
+ {agent.adapter.isMultiModal && ( +
+ Model + +
+ )} +
+)) + +export const AgentView = observer(() => { + const { + state: { resource: agent }, + } = useStore() + + return ( +
+
+
+ Agent Name + agent.update({ name: e.target.value })} + /> +
+ +
+ +
+ +
+
Default Parameters
+ +
+
+
+ ) +}) diff --git a/src/app/App.tsx b/src/app/App.tsx deleted file mode 100644 index c3c1327..0000000 --- a/src/app/App.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Sidebar } from "./Sidebar" -import { observer } from "mobx-react" -import { useStore } from "@/store" -import { ChatWithSettings } from "./ChatWithSettings" - -const App = observer(() => { - const { connected, activeChat, chats } = useStore() - - return ( -
- - {connected && activeChat && } -
- ) -}) - -export default App diff --git a/src/app/AppAccordionItem.tsx b/src/app/AppAccordionItem.tsx new file mode 100644 index 0000000..b09994a --- /dev/null +++ b/src/app/AppAccordionItem.tsx @@ -0,0 +1,21 @@ +import { observer } from "mobx-react"; +import { + AccordionContent, + AccordionItem, + AccordionTrigger +} from "@radix-ui/react-accordion"; + + +export const AppAccordionItem = observer( + ({ id, icon: Icon, title, children }) => ( + + + + {title} + + +
{children}
+
+
+ ) +); diff --git a/src/app/AppView.tsx b/src/app/AppView.tsx new file mode 100644 index 0000000..96c631a --- /dev/null +++ b/src/app/AppView.tsx @@ -0,0 +1,14 @@ +import { Sidebar } from "./Sidebar" +import { observer } from "mobx-react" +import { Router } from "./Router" + +const AppView = observer(() => { + return ( +
+ + +
+ ) +}) + +export default AppView diff --git a/src/app/AttachmentsAccordionItem.tsx b/src/app/AttachmentsAccordionItem.tsx new file mode 100644 index 0000000..552278d --- /dev/null +++ b/src/app/AttachmentsAccordionItem.tsx @@ -0,0 +1,63 @@ +import { observer } from "mobx-react" +import { ImAttachment } from "react-icons/im" +import { Input } from "@/components/ui/input" +import { processFile } from "@/lib/extraction" +import { AppAccordionItem } from "./AppAccordionItem" +import { Button } from "@/components/ui/button" + +export const AttachmentsAccordionItem = observer(({ chat }) => { + async function processFiles(files: FileList) { + const results = [] + for (const file of files) { + console.log(`Processing ${file.name}...`) + try { + const content = await processFile(file) + if (content) { + results.push({ + name: file.name, + content, + path: file.webkitRelativePath ? file.webkitRelativePath : file.name, + size: file.size, + type: file.type, + }) + console.log(file.webkitRelativePath) + } else { + console.warn(`File ${file.name} is empty`) + } + } catch (e) { + console.error(e) + } + } + return results + } + + const handleAttachment = async (e) => { + const attachments = await processFiles(e.target.files) + chat.update({ attachments }) + e.target.value = null + } + + return ( + + + + +
+ {chat.attachments.map((attachment) => ( +
+ {attachment.path} +
+ ))} +
+
+ ) +}) diff --git a/src/app/AutoTextarea.tsx b/src/app/AutoTextarea.tsx deleted file mode 100644 index ec9e8b6..0000000 --- a/src/app/AutoTextarea.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { cn } from "@/lib/utils" -import { Textarea } from "@/components/ui/textarea" -import { useRef, useState, useEffect } from "react" - -export const AutoTextarea = ({ - className, - onChange, - maxRows = 8, - ...rest -}: { - className?: string - maxRows?: number - onChange: (e: React.ChangeEvent) => void -}) => { - const ref = useRef(null) - const [currentValue, setCurrentValue] = useState("") // you can manage data with it - - useEffect(() => { - ref.current.style.height = "0px" - const scrollHeight = ref.current.scrollHeight - ref.current.style.height = `min(${ - getComputedStyle(ref.current).lineHeight - } * ${maxRows + 1}, ${scrollHeight}px)` - }, [currentValue]) - - return ( -