diff --git a/collector/processSingleFile/convert/asPDF.js b/collector/processSingleFile/convert/asPDF.js
index 560c4939f..e81fe6c76 100644
--- a/collector/processSingleFile/convert/asPDF.js
+++ b/collector/processSingleFile/convert/asPDF.js
@@ -40,9 +40,9 @@ async function asPDF({ fullFilePath = "", filename = "" }) {
const data = {
id: v4(),
url: "file://" + fullFilePath,
- title: docs[0]?.metadata?.pdf?.info?.Title || filename,
+ title: filename,
docAuthor: docs[0]?.metadata?.pdf?.info?.Creator || "no author found",
- description: "No description found.",
+ description: docs[0]?.metadata?.pdf?.info?.Title || "No description found.",
docSource: "pdf file uploaded by the user.",
chunkSource: "",
published: createdDate(fullFilePath),
diff --git a/docker/.env.example b/docker/.env.example
index ed6fd3bce..5efb2c049 100644
--- a/docker/.env.example
+++ b/docker/.env.example
@@ -27,6 +27,7 @@ GID='1000'
# LLM_PROVIDER='lmstudio'
# LMSTUDIO_BASE_PATH='http://your-server:1234/v1'
+# LMSTUDIO_MODEL_PREF='Loaded from Chat UI' # this is a bug in LMStudio 0.2.17
# LMSTUDIO_MODEL_TOKEN_LIMIT=4096
# LLM_PROVIDER='localai'
diff --git a/docker/HOW_TO_USE_DOCKER.md b/docker/HOW_TO_USE_DOCKER.md
index 9532fea0b..19a0920ef 100644
--- a/docker/HOW_TO_USE_DOCKER.md
+++ b/docker/HOW_TO_USE_DOCKER.md
@@ -109,29 +109,34 @@ container rebuilds or pulls from Docker Hub.
Your docker host will show the image as online once the build process is completed. This will build the app to `http://localhost:3001`.
-## ⚠️ Vector DB support ⚠️
+## Integrations and one-click setups
-Out of the box, all vector databases are supported. Any vector databases requiring special configuration are listed below.
+The integrations below are templates or tooling built by the community to make running the docker experience of AnythingLLM easier.
-### Using local ChromaDB with Dockerized AnythingLLM
+### Use the Midori AI Subsystem to Manage AnythingLLM
-- Ensure in your `./docker/.env` file that you have
+Follow the setup found on [Midori AI Subsystem Site](https://io.midori-ai.xyz/subsystem/manager/) for your host OS
+After setting that up install the AnythingLLM docker backend to the Midori AI Subsystem.
-```
-#./docker/.env
-...other configs
-
-VECTOR_DB="chroma"
-CHROMA_ENDPOINT='http://host.docker.internal:8000' # Allow docker to look on host port, not container.
-# CHROMA_API_HEADER="X-Api-Key" // If you have an Auth middleware on your instance.
-# CHROMA_API_KEY="sk-123abc"
-
-...other configs
-
-```
+Once that is done, you are all set!
## Common questions and fixes
+### Cannot connect to service running on localhost!
+
+If you are in docker and cannot connect to a service running on your host machine running on a local interface or loopback:
+
+- `localhost`
+- `127.0.0.1`
+- `0.0.0.0`
+
+> [!IMPORTANT]
+> On linux `http://host.docker.internal:xxxx` does not work.
+> Use `http://172.17.0.1:xxxx` instead to emulate this functionality.
+
+Then in docker you need to replace that localhost part with `host.docker.internal`. For example, if running Ollama on the host machine, bound to http://127.0.0.1:11434 you should put `http://host.docker.internal:11434` into the connection URL in AnythingLLM.
+
+
### API is not working, cannot login, LLM is "offline"?
You are likely running the docker container on a remote machine like EC2 or some other instance where the reachable URL
diff --git a/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx b/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx
index fbba7666f..200c77a6e 100644
--- a/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx
+++ b/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx
@@ -1,7 +1,14 @@
+import { useEffect, useState } from "react";
import { Info } from "@phosphor-icons/react";
import paths from "@/utils/paths";
+import System from "@/models/system";
export default function LMStudioOptions({ settings, showAlert = false }) {
+ const [basePathValue, setBasePathValue] = useState(
+ settings?.LMStudioBasePath
+ );
+ const [basePath, setBasePath] = useState(settings?.LMStudioBasePath);
+
return (
{showAlert && (
@@ -35,8 +42,11 @@ export default function LMStudioOptions({ settings, showAlert = false }) {
required={true}
autoComplete="off"
spellCheck={false}
+ onChange={(e) => setBasePathValue(e.target.value)}
+ onBlur={() => setBasePath(basePathValue)}
/>
+
Token context window
@@ -57,3 +67,73 @@ export default function LMStudioOptions({ settings, showAlert = false }) {
);
}
+
+function LMStudioModelSelection({ settings, basePath = null }) {
+ const [customModels, setCustomModels] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ async function findCustomModels() {
+ if (!basePath || !basePath.includes("/v1")) {
+ setCustomModels([]);
+ setLoading(false);
+ return;
+ }
+ setLoading(true);
+ const { models } = await System.customModels("lmstudio", null, basePath);
+ setCustomModels(models || []);
+ setLoading(false);
+ }
+ findCustomModels();
+ }, [basePath]);
+
+ if (loading || customModels.length == 0) {
+ return (
+
+
+ Chat Model Selection
+
+
+
+ {basePath?.includes("/v1")
+ ? "-- loading available models --"
+ : "-- waiting for URL --"}
+
+
+
+ );
+ }
+
+ return (
+
+
+ Chat Model Selection
+
+
+ {customModels.length > 0 && (
+
+ {customModels.map((model) => {
+ return (
+
+ {model.id}
+
+ );
+ })}
+
+ )}
+
+
+ );
+}
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FileRow/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FileRow/index.jsx
index 976c65988..e679896fa 100644
--- a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FileRow/index.jsx
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FileRow/index.jsx
@@ -4,44 +4,12 @@ import {
getFileExtension,
middleTruncate,
} from "@/utils/directories";
-import { File, Trash } from "@phosphor-icons/react";
-import System from "@/models/system";
+import { File } from "@phosphor-icons/react";
import debounce from "lodash.debounce";
-export default function FileRow({
- item,
- folderName,
- selected,
- toggleSelection,
- fetchKeys,
- setLoading,
- setLoadingMessage,
-}) {
+export default function FileRow({ item, selected, toggleSelection }) {
const [showTooltip, setShowTooltip] = useState(false);
- const onTrashClick = async (event) => {
- event.stopPropagation();
- if (
- !window.confirm(
- "Are you sure you want to delete this document?\nThis will require you to re-upload and re-embed it.\nThis document will be removed from any workspace that is currently referencing it.\nThis action is not reversible."
- )
- ) {
- return false;
- }
-
- try {
- setLoading(true);
- setLoadingMessage("This may take a while for large documents");
- await System.deleteDocument(`${folderName}/${item.name}`);
- await fetchKeys(true);
- } catch (error) {
- console.error("Failed to delete the document:", error);
- }
-
- if (selected) toggleSelection(item);
- setLoading(false);
- };
-
const handleShowTooltip = () => {
setShowTooltip(true);
};
@@ -56,11 +24,11 @@ export default function FileRow({
return (
toggleSelection(item)}
- className={`transition-all duration-200 text-white/80 text-xs grid grid-cols-12 py-2 pl-3.5 pr-8 hover:bg-sky-500/20 cursor-pointer file-row ${
+ className={`text-white/80 text-xs grid grid-cols-12 py-2 pl-3.5 pr-8 hover:bg-sky-500/20 cursor-pointer file-row ${
selected ? "selected" : ""
}`}
>
-
+
{getFileExtension(item.url)}
-
);
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FolderRow/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FolderRow/index.jsx
index 48953ab1f..46c4b7fd0 100644
--- a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FolderRow/index.jsx
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FolderRow/index.jsx
@@ -1,8 +1,7 @@
import { useState } from "react";
import FileRow from "../FileRow";
-import { CaretDown, FolderNotch, Trash } from "@phosphor-icons/react";
+import { CaretDown, FolderNotch } from "@phosphor-icons/react";
import { middleTruncate } from "@/utils/directories";
-import System from "@/models/system";
export default function FolderRow({
item,
@@ -10,36 +9,10 @@ export default function FolderRow({
onRowClick,
toggleSelection,
isSelected,
- fetchKeys,
- setLoading,
- setLoadingMessage,
autoExpanded = false,
}) {
const [expanded, setExpanded] = useState(autoExpanded);
- const onTrashClick = async (event) => {
- event.stopPropagation();
- if (
- !window.confirm(
- "Are you sure you want to delete this folder?\nThis will require you to re-upload and re-embed it.\nAny documents in this folder will be removed from any workspace that is currently referencing it.\nThis action is not reversible."
- )
- ) {
- return false;
- }
-
- try {
- setLoading(true);
- setLoadingMessage("This may take a while for large folders");
- await System.deleteFolder(item.name);
- await fetchKeys(true);
- } catch (error) {
- console.error("Failed to delete the document:", error);
- }
-
- if (selected) toggleSelection(item);
- setLoading(false);
- };
-
const handleExpandClick = (event) => {
event.stopPropagation();
setExpanded(!expanded);
@@ -49,7 +22,7 @@ export default function FolderRow({
<>
@@ -59,6 +32,10 @@ export default function FolderRow({
role="checkbox"
aria-checked={selected}
tabIndex={0}
+ onClick={(event) => {
+ event.stopPropagation();
+ toggleSelection(item);
+ }}
>
{selected &&
}
@@ -75,35 +52,23 @@ export default function FolderRow({
weight="fill"
/>
- {middleTruncate(item.name, 40)}
+ {middleTruncate(item.name, 35)}
-
- {item.name !== "custom-documents" && (
-
- )}
-
{expanded && (
-
+ <>
{item.items.map((fileItem) => (
))}
-
+ >
)}
>
);
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FolderSelectionPopup/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FolderSelectionPopup/index.jsx
new file mode 100644
index 000000000..2ebfcde2d
--- /dev/null
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/FolderSelectionPopup/index.jsx
@@ -0,0 +1,24 @@
+import { middleTruncate } from "@/utils/directories";
+
+export default function FolderSelectionPopup({ folders, onSelect, onClose }) {
+ const handleFolderSelect = (folder) => {
+ onSelect(folder);
+ onClose();
+ };
+
+ return (
+
+
+ {folders.map((folder) => (
+ handleFolderSelect(folder)}
+ className="px-4 py-2 text-xs text-gray-700 hover:bg-gray-200 rounded-lg cursor-pointer whitespace-nowrap"
+ >
+ {middleTruncate(folder.name, 25)}
+
+ ))}
+
+
+ );
+}
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/MoveToFolderIcon.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/MoveToFolderIcon.jsx
new file mode 100644
index 000000000..3916fc771
--- /dev/null
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/MoveToFolderIcon.jsx
@@ -0,0 +1,44 @@
+export default function MoveToFolderIcon({
+ className,
+ width = 18,
+ height = 18,
+}) {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx
index 95a1ecd07..83544f72d 100644
--- a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx
@@ -2,11 +2,16 @@ import UploadFile from "../UploadFile";
import PreLoader from "@/components/Preloader";
import { memo, useEffect, useState } from "react";
import FolderRow from "./FolderRow";
-import pluralize from "pluralize";
import System from "@/models/system";
+import { Plus, Trash } from "@phosphor-icons/react";
+import Document from "@/models/document";
+import showToast from "@/utils/toast";
+import FolderSelectionPopup from "./FolderSelectionPopup";
+import MoveToFolderIcon from "./MoveToFolderIcon";
function Directory({
files,
+ setFiles,
loading,
setLoading,
workspace,
@@ -19,12 +24,19 @@ function Directory({
loadingMessage,
}) {
const [amountSelected, setAmountSelected] = useState(0);
+ const [newFolderName, setNewFolderName] = useState("");
+ const [showNewFolderInput, setShowNewFolderInput] = useState(false);
+ const [showFolderSelection, setShowFolderSelection] = useState(false);
+
+ useEffect(() => {
+ setAmountSelected(Object.keys(selectedItems).length);
+ }, [selectedItems]);
const deleteFiles = async (event) => {
event.stopPropagation();
if (
!window.confirm(
- "Are you sure you want to delete these files?\nThis will remove the files from the system and remove them from any existing workspaces automatically.\nThis action is not reversible."
+ "Are you sure you want to delete these files and folders?\nThis will remove the files from the system and remove them from any existing workspaces automatically.\nThis action is not reversible."
)
) {
return false;
@@ -32,6 +44,8 @@ function Directory({
try {
const toRemove = [];
+ const foldersToRemove = [];
+
for (const itemId of Object.keys(selectedItems)) {
for (const folder of files.items) {
const foundItem = folder.items.find((file) => file.id === itemId);
@@ -41,13 +55,29 @@ function Directory({
}
}
}
+ for (const folder of files.items) {
+ if (folder.name === "custom-documents") {
+ continue;
+ }
+
+ if (isSelected(folder.id, folder)) {
+ foldersToRemove.push(folder.name);
+ }
+ }
+
setLoading(true);
- setLoadingMessage(`Removing ${toRemove.length} documents. Please wait.`);
+ setLoadingMessage(
+ `Removing ${toRemove.length} documents and ${foldersToRemove.length} folders. Please wait.`
+ );
await System.deleteDocuments(toRemove);
+ for (const folderName of foldersToRemove) {
+ await System.deleteFolder(folderName);
+ }
+
await fetchKeys(true);
setSelectedItems({});
} catch (error) {
- console.error("Failed to delete the document:", error);
+ console.error("Failed to delete files and folders:", error);
} finally {
setLoading(false);
setSelectedItems({});
@@ -57,15 +87,17 @@ function Directory({
const toggleSelection = (item) => {
setSelectedItems((prevSelectedItems) => {
const newSelectedItems = { ...prevSelectedItems };
-
if (item.type === "folder") {
- const isCurrentlySelected = isFolderCompletelySelected(item);
- if (isCurrentlySelected) {
+ // select all files in the folder
+ if (newSelectedItems[item.name]) {
+ delete newSelectedItems[item.name];
item.items.forEach((file) => delete newSelectedItems[file.id]);
} else {
+ newSelectedItems[item.name] = true;
item.items.forEach((file) => (newSelectedItems[file.id] = true));
}
} else {
+ // single file selections
if (newSelectedItems[item.id]) {
delete newSelectedItems[item.id];
} else {
@@ -77,44 +109,124 @@ function Directory({
});
};
- const isFolderCompletelySelected = (folder) => {
- if (folder.items.length === 0) {
- return false;
- }
- return folder.items.every((file) => selectedItems[file.id]);
- };
-
+ // check if item is selected based on selectedItems state
const isSelected = (id, item) => {
if (item && item.type === "folder") {
- return isFolderCompletelySelected(item);
+ if (!selectedItems[item.name]) {
+ return false;
+ }
+ return item.items.every((file) => selectedItems[file.id]);
}
return !!selectedItems[id];
};
- useEffect(() => {
- setAmountSelected(Object.keys(selectedItems).length);
- }, [selectedItems]);
+ const createNewFolder = () => {
+ setShowNewFolderInput(true);
+ };
+
+ const confirmNewFolder = async () => {
+ if (newFolderName.trim() !== "") {
+ const newFolder = {
+ name: newFolderName,
+ type: "folder",
+ items: [],
+ };
+
+ // If folder failed to create - silently fail.
+ const { success } = await Document.createFolder(newFolderName);
+ if (success) {
+ setFiles({
+ ...files,
+ items: [...files.items, newFolder],
+ });
+ }
+
+ setNewFolderName("");
+ setShowNewFolderInput(false);
+ }
+ };
+
+ const moveToFolder = async (folder) => {
+ const toMove = [];
+ for (const itemId of Object.keys(selectedItems)) {
+ for (const currentFolder of files.items) {
+ const foundItem = currentFolder.items.find(
+ (file) => file.id === itemId
+ );
+ if (foundItem) {
+ toMove.push({ ...foundItem, folderName: currentFolder.name });
+ break;
+ }
+ }
+ }
+ setLoading(true);
+ setLoadingMessage(`Moving ${toMove.length} documents. Please wait.`);
+ const { success, message } = await Document.moveToFolder(
+ toMove,
+ folder.name
+ );
+ if (!success) {
+ showToast(`Error moving files: ${message}`, "error");
+ setLoading(false);
+ return;
+ }
+
+ if (success && message) {
+ // show info if some files were not moved due to being embedded
+ showToast(message, "info");
+ } else {
+ showToast(`Successfully moved ${toMove.length} documents.`, "success");
+ }
+ await fetchKeys(true);
+ setSelectedItems({});
+ setLoading(false);
+ };
return (
-
+
My Documents
+ {showNewFolderInput ? (
+
+
setNewFolderName(e.target.value)}
+ className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-md p-2.5 w-[150px] h-[32px]"
+ />
+
+
+ Create
+
+
+
+ ) : (
+
+
+
+ New Folder
+
+
+ )}
-
-
-
Name
+
+
+
Name
Date
Kind
-
Cached
-
+
{loading ? (
@@ -122,11 +234,10 @@ function Directory({
{loadingMessage}
- ) : !!files.items ? (
+ ) : files.items ? (
files.items.map(
(item, index) =>
- (item.name === "custom-documents" ||
- (item.type === "folder" && item.items.length > 0)) && (
+ item.type === "folder" && (
toggleSelection(item)}
toggleSelection={toggleSelection}
isSelected={isSelected}
- setLoading={setLoading}
- setLoadingMessage={setLoadingMessage}
autoExpanded={index === 0}
/>
)
@@ -152,26 +260,45 @@ function Directory({
)}
-
{amountSelected !== 0 && (
-
-
-
setHighlightWorkspace(true)}
- onMouseLeave={() => setHighlightWorkspace(false)}
- onClick={moveToWorkspace}
- className="border-none text-sm font-semibold h-7 px-2.5 rounded-lg hover:text-white hover:bg-neutral-800/80 flex items-center"
- >
- Move {amountSelected} {pluralize("file", amountSelected)} to
- workspace
-
+
+
+
+
setHighlightWorkspace(true)}
+ onMouseLeave={() => setHighlightWorkspace(false)}
+ className="border-none text-sm font-semibold bg-white h-[30px] px-2.5 rounded-lg hover:text-white hover:bg-neutral-800/80"
+ >
+ Move to Workspace
+
+
+
+ setShowFolderSelection(!showFolderSelection)
+ }
+ className="border-none text-sm font-semibold bg-white h-[32px] w-[32px] rounded-lg text-[#222628] hover:bg-neutral-800/80 flex justify-center items-center group"
+ >
+
+
+ {showFolderSelection && (
+ item.type === "folder"
+ )}
+ onSelect={moveToFolder}
+ onClose={() => setShowFolderSelection(false)}
+ />
+ )}
+
+
+
+
+
-
- Delete
-
)}
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/UploadFile/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/UploadFile/index.jsx
index b83aadc2f..acf319d92 100644
--- a/frontend/src/components/Modals/MangeWorkspace/Documents/UploadFile/index.jsx
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/UploadFile/index.jsx
@@ -76,7 +76,7 @@ export default function UploadFile({ workspace, fetchKeys, setLoading }) {
return (
{fetchingUrl ? "Fetching..." : "Fetch website"}
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx
index f73916290..f47325427 100644
--- a/frontend/src/components/Modals/MangeWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx
@@ -53,7 +53,7 @@ export default function WorkspaceFileRow({
const handleMouseLeave = debounce(handleHideTooltip, 500);
return (
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/WorkspaceDirectory/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/WorkspaceDirectory/index.jsx
index 68a5005a4..4cfa55a5d 100644
--- a/frontend/src/components/Modals/MangeWorkspace/Documents/WorkspaceDirectory/index.jsx
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/WorkspaceDirectory/index.jsx
@@ -55,7 +55,7 @@ function WorkspaceDirectory({
@@ -96,7 +96,7 @@ function WorkspaceDirectory({
{hasChanges && (
-
+
{embeddingCosts === 0
@@ -114,7 +114,7 @@ function WorkspaceDirectory({
Save and Embed
@@ -177,7 +177,7 @@ const PinAlert = memo(() => {
Okay, got it
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx
index 736a1476f..12784299e 100644
--- a/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx
@@ -191,9 +191,10 @@ export default function DocumentSettings({ workspace, systemSettings }) {
};
return (
-
+
-
+
{
return (
-
+
-
+
+
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx
index 52e870123..d424c906f 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useRef } from "react";
+import React, { useState, useRef, useEffect } from "react";
import SlashCommandsButton, {
SlashCommands,
useSlashCommands,
@@ -7,9 +7,7 @@ import { isMobile } from "react-device-detect";
import debounce from "lodash.debounce";
import { PaperPlaneRight } from "@phosphor-icons/react";
import StopGenerationButton from "./StopGenerationButton";
-
export default function PromptInput({
- workspace,
message,
submit,
onChange,
@@ -19,13 +17,27 @@ export default function PromptInput({
}) {
const { showSlashCommand, setShowSlashCommand } = useSlashCommands();
const formRef = useRef(null);
+ const textareaRef = useRef(null);
const [_, setFocused] = useState(false);
+ useEffect(() => {
+ if (!inputDisabled && textareaRef.current) {
+ textareaRef.current.focus();
+ }
+ resetTextAreaHeight();
+ }, [inputDisabled]);
+
const handleSubmit = (e) => {
setFocused(false);
submit(e);
};
+ const resetTextAreaHeight = () => {
+ if (textareaRef.current) {
+ textareaRef.current.style.height = "auto";
+ }
+ };
+
const checkForSlash = (e) => {
const input = e.target.value;
if (input === "/") setShowSlashCommand(true);
@@ -44,14 +56,12 @@ export default function PromptInput({
const adjustTextArea = (event) => {
if (isMobile) return false;
const element = event.target;
- element.style.height = "1px";
- element.style.height =
- event.target.value.length !== 0
- ? 25 + element.scrollHeight + "px"
- : "1px";
+ element.style.height = "auto";
+ element.style.height = `${element.scrollHeight}px`;
};
const watchForSlash = debounce(checkForSlash, 300);
+
return (