Merge branch 'cz/mergeAll' of https://github.com/langflow-ai/langflow into cz/mergeAll

This commit is contained in:
cristhianzl 2024-06-07 19:53:29 -03:00
commit 817f85cfaf
11 changed files with 78 additions and 20 deletions

View file

@ -1,10 +1,12 @@
import TableAutoCellRender from "../tableComponent/components/tableAutoCellRender";
export default function ArrayReader({ array }: { array: any[] }): JSX.Element { export default function ArrayReader({ array }: { array: any[] }): JSX.Element {
//TODO check array type //TODO check array type
return ( return (
<div> <div>
<ul> <ul>
{array.map((item, index) => ( {array.map((item, index) => (
<li key={index}>{item}</li> <li key={index}>{<TableAutoCellRender value={item} />}</li>
))} ))}
</ul> </ul>
</div> </div>

View file

@ -13,7 +13,9 @@ function RecordsOutputComponent({
rows: any; rows: any;
columnMode?: "intersection" | "union"; columnMode?: "intersection" | "union";
}) { }) {
console.log("rows", rows);
const columns = extractColumnsFromRows(rows, columnMode); const columns = extractColumnsFromRows(rows, columnMode);
console.log("columns", columns);
const columnDefs = columns.map((col, idx) => ({ const columnDefs = columns.map((col, idx) => ({
...col, ...col,

View file

@ -9,20 +9,17 @@ import { Badge } from "../../../ui/badge";
export default function TableAutoCellRender({ export default function TableAutoCellRender({
value, value,
}: CustomCellRendererProps) { }: CustomCellRendererProps | { value: any }) {
function getCellType() { function getCellType() {
switch (typeof value) { switch (typeof value) {
case "object": case "object":
if (value === null) { if (value === null) {
return String(value); return String(value);
} else if (Array.isArray(value)) { } else if (Array.isArray(value)) {
return <ArrayReader array={value} />; return <ObjectRender object={value} />;
} else if (value.definitions) {
// use a custom render defined by the sender
} else { } else {
return <ObjectRender object={value} />; return <ObjectRender object={value} />;
} }
break;
case "string": case "string":
if (isTimeStampString(value)) { if (isTimeStampString(value)) {
return <DateReader date={value} />; return <DateReader date={value} />;

View file

@ -5,17 +5,20 @@ const UploadFileButton = ({
fileInputRef, fileInputRef,
handleFileChange, handleFileChange,
handleButtonClick, handleButtonClick,
lockChat,
}) => { }) => {
return ( return (
<div> <div>
<input <input
disabled={lockChat}
type="file" type="file"
ref={fileInputRef} ref={fileInputRef}
style={{ display: "none" }} style={{ display: "none" }}
onChange={handleFileChange} onChange={handleFileChange}
/> />
<Button <Button
className="font-bold text-white transition-all hover:text-muted-foreground" disabled={lockChat}
className={`font-bold text-white transition-all ${lockChat ? "cursor-not-allowed" : "hover:text-muted-foreground"}`}
onClick={handleButtonClick} onClick={handleButtonClick}
variant="none" variant="none"
size="none" size="none"

View file

@ -1,13 +1,30 @@
import ShortUniqueId from "short-unique-id"; import ShortUniqueId from "short-unique-id";
import useFileUpload from "./use-file-upload"; import useFileUpload from "./use-file-upload";
import useAlertStore from "../../../../../../stores/alertStore";
const fsErrorText =
"Please ensure your file has one of the following extensions:";
const snErrorTxt = "png, jpg, jpeg, gif, bmp, webp";
export const useHandleFileChange = (setFiles, currentFlowId) => { export const useHandleFileChange = (setFiles, currentFlowId) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
const handleFileChange = async ( const handleFileChange = async (
event: React.ChangeEvent<HTMLInputElement>, event: React.ChangeEvent<HTMLInputElement>,
) => { ) => {
const fileInput = event.target; const fileInput = event.target;
const file = fileInput.files?.[0]; const file = fileInput.files?.[0];
if (file) { if (file) {
const allowedExtensions = ["png", "jpg", "jpeg", "gif", "bmp", "webp"];
const fileExtension = file.name.split(".").pop()?.toLowerCase();
if (!fileExtension || !allowedExtensions.includes(fileExtension)) {
setErrorData({
title: "Error uploading file",
list: [fsErrorText, snErrorTxt],
});
return;
}
const uid = new ShortUniqueId({ length: 10 }); // Increase the length to ensure uniqueness const uid = new ShortUniqueId({ length: 10 }); // Increase the length to ensure uniqueness
const id = uid(); const id = uid();
const type = file.type.split("/")[0]; const type = file.type.split("/")[0];

View file

@ -1,10 +1,19 @@
import { useEffect } from "react"; import { useEffect } from "react";
import ShortUniqueId from "short-unique-id"; import ShortUniqueId from "short-unique-id";
import useFileUpload from "./use-file-upload"; import useFileUpload from "./use-file-upload";
import useAlertStore from "../../../../../../stores/alertStore";
const useUpload = (uploadFile, currentFlowId, setFiles) => { const fsErrorText =
"Please ensure your file has one of the following extensions:";
const snErrorTxt = "png, jpg, jpeg, gif, bmp, webp";
const useUpload = (uploadFile, currentFlowId, setFiles, lockChat) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
useEffect(() => { useEffect(() => {
const handlePaste = (event: ClipboardEvent): void => { const handlePaste = (event: ClipboardEvent): void => {
if (lockChat) {
return;
}
const items = event.clipboardData?.items; const items = event.clipboardData?.items;
if (items) { if (items) {
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
@ -12,6 +21,23 @@ const useUpload = (uploadFile, currentFlowId, setFiles) => {
const uid = new ShortUniqueId({ length: 3 }); const uid = new ShortUniqueId({ length: 3 });
const blob = items[i].getAsFile(); const blob = items[i].getAsFile();
if (blob) { if (blob) {
const allowedExtensions = [
"png",
"jpg",
"jpeg",
"gif",
"bmp",
"webp",
];
const fileExtension = blob.name.split(".").pop()?.toLowerCase();
if (!fileExtension || !allowedExtensions.includes(fileExtension)) {
setErrorData({
title: "Error uploading file",
list: [fsErrorText, snErrorTxt],
});
return;
}
const id = uid(); const id = uid();
setFiles((prevFiles) => [ setFiles((prevFiles) => [
...prevFiles, ...prevFiles,
@ -27,7 +53,7 @@ const useUpload = (uploadFile, currentFlowId, setFiles) => {
return () => { return () => {
document.removeEventListener("paste", handlePaste); document.removeEventListener("paste", handlePaste);
}; };
}, [uploadFile, currentFlowId]); }, [uploadFile, currentFlowId, lockChat]);
return null; return null;
}; };

View file

@ -37,7 +37,7 @@ export default function ChatInput({
useFocusOnUnlock(lockChat, inputRef); useFocusOnUnlock(lockChat, inputRef);
useAutoResizeTextArea(chatValue, inputRef); useAutoResizeTextArea(chatValue, inputRef);
useUpload(uploadFile, currentFlowId, setFiles); useUpload(uploadFile, currentFlowId, setFiles, lockChat || saveLoading);
const { handleFileChange } = useHandleFileChange(setFiles, currentFlowId); const { handleFileChange } = useHandleFileChange(setFiles, currentFlowId);
const send = () => { const send = () => {
@ -92,8 +92,11 @@ export default function ChatInput({
/> />
</div> </div>
<div className="absolute bottom-2 left-4"> <div
className={`absolute bottom-2 left-4 ${lockChat || saveLoading ? "cursor-not-allowed" : ""}`}
>
<UploadFileButton <UploadFileButton
lockChat={lockChat || saveLoading}
fileInputRef={fileInputRef} fileInputRef={fileInputRef}
handleFileChange={handleFileChange} handleFileChange={handleFileChange}
handleButtonClick={handleButtonClick} handleButtonClick={handleButtonClick}
@ -110,7 +113,7 @@ export default function ChatInput({
key={file.id} key={file.id}
onDelete={() => { onDelete={() => {
setFiles((prev: FilePreviewType[]) => setFiles((prev: FilePreviewType[]) =>
prev.filter((f) => f.id !== file.id) prev.filter((f) => f.id !== file.id),
); );
// TODO: delete file on backend // TODO: delete file on backend
}} }}

View file

@ -1,4 +1,5 @@
import ForwardedIconComponent from "../../../../../../../components/genericIconComponent"; import ForwardedIconComponent from "../../../../../../../components/genericIconComponent";
import { Button } from "../../../../../../../components/ui/button";
export default function DownloadButton({ export default function DownloadButton({
isHovered, isHovered,
@ -10,14 +11,18 @@ export default function DownloadButton({
if (isHovered) { if (isHovered) {
return ( return (
<div <div
className={`absolute right-1 top-1 rounded-bl-lg bg-muted text-sm font-bold text-foreground `} className={`absolute right-1 top-1 rounded-md bg-muted text-sm font-bold text-foreground `}
> >
<button className="px-2 py-1 text-ring " onClick={handleDownload}> <Button
variant={"none"}
className="bg-transparent px-2 py-1 text-ring"
onClick={handleDownload}
>
<ForwardedIconComponent <ForwardedIconComponent
name="DownloadCloud" name="DownloadCloud"
className="h-5 w-5 text-current hover:scale-110" className="h-5 w-5 bg-transparent text-current hover:scale-110"
/> />
</button> </Button>
</div> </div>
); );
} }

View file

@ -8,7 +8,7 @@ import DownloadButton from "./components/downloadButton/downloadButton";
import getClasses from "./utils/get-classes"; import getClasses from "./utils/get-classes";
import handleDownload from "./utils/handle-download"; import handleDownload from "./utils/handle-download";
const imgTypes = new Set(["png", "jpg"]); const imgTypes = new Set(["png", "jpg", "jpeg", "gif", "webp", "image"]);
export default function FileCard({ export default function FileCard({
fileName, fileName,
@ -29,7 +29,7 @@ export default function FileCard({
const imgSrc = `${BACKEND_URL.slice( const imgSrc = `${BACKEND_URL.slice(
0, 0,
BACKEND_URL.length - 1 BACKEND_URL.length - 1,
)}${BASE_URL_API}files/images/${content}`; )}${BASE_URL_API}files/images/${content}`;
if (showFile) { if (showFile) {

View file

@ -5,6 +5,8 @@ import IconComponent, {
import { Skeleton } from "../../../../../components/ui/skeleton"; import { Skeleton } from "../../../../../components/ui/skeleton";
import formatFileName from "./utils/format-file-name"; import formatFileName from "./utils/format-file-name";
const supImgFiles = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "image"];
export default function FilePreview({ export default function FilePreview({
error, error,
file, file,
@ -16,7 +18,8 @@ export default function FilePreview({
error: boolean; error: boolean;
onDelete: () => void; onDelete: () => void;
}) { }) {
const isImage = file.type.toLowerCase().includes("image"); const fileType = file.type.toLowerCase();
const isImage = supImgFiles.some((type) => fileType.includes(type));
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);

View file

@ -1,7 +1,6 @@
import { ColDef, ColGroupDef } from "ag-grid-community"; import { ColDef, ColGroupDef } from "ag-grid-community";
import clsx, { ClassValue } from "clsx"; import clsx, { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import TableAutoCellRender from "../components/tableComponent/components/tableAutoCellRender";
import { APIDataType, TemplateVariableType } from "../types/api"; import { APIDataType, TemplateVariableType } from "../types/api";
import { import {
groupedObjType, groupedObjType,
@ -10,6 +9,7 @@ import {
} from "../types/components"; } from "../types/components";
import { NodeType } from "../types/flow"; import { NodeType } from "../types/flow";
import { FlowState } from "../types/tabs"; import { FlowState } from "../types/tabs";
import TableAutoCellRender from "../components/tableComponent/components/tableAutoCellRender";
export function classNames(...classes: Array<string>): string { export function classNames(...classes: Array<string>): string {
return classes.filter(Boolean).join(" "); return classes.filter(Boolean).join(" ");