Feature/view mode (#769)

This feature implements a view mode to all of the flows, to embed on
sites.
This commit is contained in:
Lucas Oliveira 2023-08-15 12:43:29 -03:00 • committed by GitHub
commit 2b21db32b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 200 additions and 141 deletions

View file

@ -9,7 +9,6 @@ import ErrorAlert from "./alerts/error";
import NoticeAlert from "./alerts/notice"; import NoticeAlert from "./alerts/notice";
import SuccessAlert from "./alerts/success"; import SuccessAlert from "./alerts/success";
import CrashErrorComponent from "./components/CrashErrorComponent"; import CrashErrorComponent from "./components/CrashErrorComponent";
import Header from "./components/headerComponent";
import { alertContext } from "./contexts/alertContext"; import { alertContext } from "./contexts/alertContext";
import { locationContext } from "./contexts/locationContext"; import { locationContext } from "./contexts/locationContext";
import { TabsContext } from "./contexts/tabsContext"; import { TabsContext } from "./contexts/tabsContext";
@ -133,7 +132,6 @@ export default function App() {
}} }}
FallbackComponent={CrashErrorComponent} FallbackComponent={CrashErrorComponent}
> >
<Header />
<Router /> <Router />
</ErrorBoundary> </ErrorBoundary>
<div></div> <div></div>

View file

@ -6,6 +6,7 @@ import { TabsContext } from "../../contexts/tabsContext";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { CardComponent } from "../../components/cardComponent"; import { CardComponent } from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent";
import { getExamples } from "../../controllers/API"; import { getExamples } from "../../controllers/API";
import { FlowType } from "../../types/flow"; import { FlowType } from "../../types/flow";
export default function CommunityPage() { export default function CommunityPage() {
@ -42,61 +43,65 @@ export default function CommunityPage() {
handleExamples(); handleExamples();
}, []); }, []);
return ( return (
<div className="community-page-arrangement"> <>
<div className="community-page-nav-arrangement"> <Header />
<span className="community-page-nav-title">
<IconComponent name="Users2" className="w-6" /> <div className="community-page-arrangement">
Community Examples <div className="community-page-nav-arrangement">
<span className="community-page-nav-title">
<IconComponent name="Users2" className="w-6" />
Community Examples
</span>
<div className="community-page-nav-button">
<a
href="https://github.com/logspace-ai/langflow_examples"
target="_blank"
rel="noreferrer"
>
<Button variant="primary">
<IconComponent
name="GithubIcon"
className="main-page-nav-button"
/>
Add Your Example
</Button>
</a>
</div>
</div>
<span className="community-page-description-text">
Discover and learn from shared examples by the Langflow community. We
welcome new example contributions that can help our community explore
new and powerful features.
</span> </span>
<div className="community-page-nav-button"> <div className="community-pages-flows-panel">
<a {!loadingExamples &&
href="https://github.com/logspace-ai/langflow_examples" examples.map((flow, idx) => (
target="_blank" <CardComponent
rel="noreferrer" key={idx}
> flow={flow}
<Button variant="primary"> id={flow.id}
<IconComponent button={
name="GithubIcon" <Button
className="main-page-nav-button" variant="outline"
size="sm"
className="whitespace-nowrap "
onClick={() => {
addFlow(flow, true).then((id) => {
navigate("/flow/" + id);
});
}}
>
<IconComponent
name="GitFork"
className="main-page-nav-button"
/>
Fork Example
</Button>
}
/> />
Add Your Example ))}
</Button>
</a>
</div> </div>
</div> </div>
<span className="community-page-description-text"> </>
Discover and learn from shared examples by the Langflow community. We
welcome new example contributions that can help our community explore
new and powerful features.
</span>
<div className="community-pages-flows-panel">
{!loadingExamples &&
examples.map((flow, idx) => (
<CardComponent
key={idx}
flow={flow}
id={flow.id}
button={
<Button
variant="outline"
size="sm"
className="whitespace-nowrap "
onClick={() => {
addFlow(flow, true).then((id) => {
navigate("/flow/" + id);
});
}}
>
<IconComponent
name="GitFork"
className="main-page-nav-button"
/>
Fork Example
</Button>
}
/>
))}
</div>
</div>
); );
} }

View file

@ -35,7 +35,13 @@ const nodeTypes = {
genericNode: GenericNode, genericNode: GenericNode,
}; };
export default function Page({ flow }: { flow: FlowType }) { export default function Page({
flow,
view,
}: {
flow: FlowType;
view?: boolean;
}) {
let { let {
updateFlow, updateFlow,
uploadFlow, uploadFlow,
@ -357,7 +363,7 @@ export default function Page({ flow }: { flow: FlowType }) {
return ( return (
<div className="flex h-full overflow-hidden"> <div className="flex h-full overflow-hidden">
<ExtraSidebar /> {!view && <ExtraSidebar />}
{/* Main area */} {/* Main area */}
<main className="flex flex-1"> <main className="flex flex-1">
{/* Primary column */} {/* Primary column */}
@ -399,14 +405,21 @@ export default function Page({ flow }: { flow: FlowType }) {
className="theme-attribution" className="theme-attribution"
minZoom={0.01} minZoom={0.01}
maxZoom={8} maxZoom={8}
zoomOnScroll={!view}
zoomOnPinch={!view}
panOnDrag={!view}
> >
<Background className="" /> <Background className="" />
<Controls {!view && (
className="bg-muted fill-foreground stroke-foreground text-primary <Controls
className="bg-muted fill-foreground stroke-foreground text-primary
[&>button]:border-b-border hover:[&>button]:bg-border" [&>button]:border-b-border hover:[&>button]:bg-border"
></Controls> ></Controls>
)}
</ReactFlow> </ReactFlow>
<Chat flow={flow} reactFlowInstance={reactFlowInstance} /> {!view && (
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
)}
</div> </div>
) : ( ) : (
<></> <></>

View file

@ -1,5 +1,6 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import Header from "../../components/headerComponent";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { getVersion } from "../../controllers/API"; import { getVersion } from "../../controllers/API";
import Page from "./components/PageComponent"; import Page from "./components/PageComponent";
@ -22,20 +23,23 @@ export default function FlowPage() {
}, []); }, []);
return ( return (
<div className="flow-page-positioning"> <>
{flows.length > 0 && <Header />
tabId !== "" && <div className="flow-page-positioning">
flows.findIndex((flow) => flow.id === tabId) !== -1 && ( {flows.length > 0 &&
<Page flow={flows.find((flow) => flow.id === tabId)} /> tabId !== "" &&
)} flows.findIndex((flow) => flow.id === tabId) !== -1 && (
<a <Page flow={flows.find((flow) => flow.id === tabId)} />
target={"_blank"} )}
href="https://logspace.ai/" <a
className="logspace-page-icon" target={"_blank"}
> href="https://logspace.ai/"
{version && <div className="mt-1">⛓️ Langflow v{version}</div>} className="logspace-page-icon"
<div className={version ? "mt-2" : "mt-1"}>Created by Logspace</div> >
</a> {version && <div className="mt-1">⛓️ Langflow v{version}</div>}
</div> <div className={version ? "mt-2" : "mt-1"}>Created by Logspace</div>
</a>
</div>
</>
); );
} }

View file

@ -2,6 +2,7 @@ import { useContext, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { CardComponent } from "../../components/cardComponent"; import { CardComponent } from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { USER_PROJECTS_HEADER } from "../../constants/constants"; import { USER_PROJECTS_HEADER } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
@ -17,74 +18,77 @@ export default function HomePage() {
// Personal flows display // Personal flows display
return ( return (
<div className="main-page-panel"> <>
<div className="main-page-nav-arrangement"> <Header />
<span className="main-page-nav-title"> <div className="main-page-panel">
<IconComponent name="Home" className="w-6" /> <div className="main-page-nav-arrangement">
{USER_PROJECTS_HEADER} <span className="main-page-nav-title">
<IconComponent name="Home" className="w-6" />
{USER_PROJECTS_HEADER}
</span>
<div className="button-div-style">
<Button
variant="primary"
onClick={() => {
downloadFlows();
}}
>
<IconComponent name="Download" className="main-page-nav-button" />
Download Collection
</Button>
<Button
variant="primary"
onClick={() => {
uploadFlows();
}}
>
<IconComponent name="Upload" className="main-page-nav-button" />
Upload Collection
</Button>
<Button
variant="primary"
onClick={() => {
addFlow(null, true).then((id) => {
navigate("/flow/" + id);
});
}}
>
<IconComponent name="Plus" className="main-page-nav-button" />
New Project
</Button>
</div>
</div>
<span className="main-page-description-text">
Manage your personal projects. Download or upload your collection.
</span> </span>
<div className="button-div-style"> <div className="main-page-flows-display">
<Button {flows.map((flow, idx) => (
variant="primary" <CardComponent
onClick={() => { key={idx}
downloadFlows(); flow={flow}
}} id={flow.id}
> button={
<IconComponent name="Download" className="main-page-nav-button" /> <Link to={"/flow/" + flow.id}>
Download Collection <Button
</Button> variant="outline"
<Button size="sm"
variant="primary" className="whitespace-nowrap "
onClick={() => { >
uploadFlows(); <IconComponent
}} name="ExternalLink"
> className="main-page-nav-button"
<IconComponent name="Upload" className="main-page-nav-button" /> />
Upload Collection Edit Flow
</Button> </Button>
<Button </Link>
variant="primary" }
onClick={() => { onDelete={() => {
addFlow(null, true).then((id) => { removeFlow(flow.id);
navigate("/flow/" + id); }}
}); />
}} ))}
>
<IconComponent name="Plus" className="main-page-nav-button" />
New Project
</Button>
</div> </div>
</div> </div>
<span className="main-page-description-text"> </>
Manage your personal projects. Download or upload your collection.
</span>
<div className="main-page-flows-display">
{flows.map((flow, idx) => (
<CardComponent
key={idx}
flow={flow}
id={flow.id}
button={
<Link to={"/flow/" + flow.id}>
<Button
variant="outline"
size="sm"
className="whitespace-nowrap "
>
<IconComponent
name="ExternalLink"
className="main-page-nav-button"
/>
Edit Flow
</Button>
</Link>
}
onDelete={() => {
removeFlow(flow.id);
}}
/>
))}
</div>
</div>
); );
} }

View file

@ -0,0 +1,33 @@
import { useContext, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { TabsContext } from "../../contexts/tabsContext";
import { getVersion } from "../../controllers/API";
import Page from "../FlowPage/components/PageComponent";
export default function ViewPage() {
const { flows, tabId, setTabId } = useContext(TabsContext);
const { id } = useParams();
// Set flow tab id
useEffect(() => {
setTabId(id);
}, [id]);
// Initialize state variable for the version
const [version, setVersion] = useState("");
useEffect(() => {
getVersion().then((data) => {
setVersion(data.version);
});
}, []);
return (
<div className="flow-page-positioning">
{flows.length > 0 &&
tabId !== "" &&
flows.findIndex((flow) => flow.id === tabId) !== -1 && (
<Page view flow={flows.find((flow) => flow.id === tabId)} />
)}
</div>
);
}

View file

@ -2,6 +2,7 @@ import { Route, Routes } from "react-router-dom";
import CommunityPage from "./pages/CommunityPage"; import CommunityPage from "./pages/CommunityPage";
import FlowPage from "./pages/FlowPage"; import FlowPage from "./pages/FlowPage";
import HomePage from "./pages/MainPage"; import HomePage from "./pages/MainPage";
import ViewPage from "./pages/ViewPage";
const Router = () => { const Router = () => {
return ( return (
@ -10,6 +11,7 @@ const Router = () => {
<Route path="/community" element={<CommunityPage />} /> <Route path="/community" element={<CommunityPage />} />
<Route path="/flow/:id/"> <Route path="/flow/:id/">
<Route path="" element={<FlowPage />} /> <Route path="" element={<FlowPage />} />
<Route path="view" element={<ViewPage />} />
</Route> </Route>
<Route path="*" element={<HomePage />} /> <Route path="*" element={<HomePage />} />
</Routes> </Routes>