Compare commits

..
Author SHA1 Message Date
EmmanuelMr18 fd150aa20f fix: ctrl+click/cmd+click to open a item of the menu 2024-02-16 23:16:07 -06:00
Syn 17653508c5 Self Host (#27)
* Update README.md

* Update README.md
2024-02-17 13:09:26 +08:00
Emmanuel Morales ace12efd1a fix(workflows): user can click multiple times on run even while loading (#26) 2024-02-15 14:39:20 +08:00
5 changed files with 30 additions and 77 deletions
+4
View File
@@ -93,6 +93,10 @@ Major areas
# Self Hosting with Vercel # Self Hosting with Vercel
[![Video](https://img.mytsi.org/i/nFOG479.png)](https://www.youtube.com/watch?v=hWvsEY1cS2M)
Tutorial Created by [Ross](https://github.com/rossman22590) and [Syn](https://github.com/mortlsyn)
Build command Build command
``` ```
+1 -3
View File
@@ -80,9 +80,7 @@
/* @apply rounded-lg p-2 overflow-x-scroll */ /* @apply rounded-lg p-2 overflow-x-scroll */
@apply p-2 max-w-full overflow-auto w-full @apply p-2 max-w-full overflow-auto w-full
} }
.vsc-controller{
position: absolute;
}
@layer base { @layer base {
* { * {
@apply border-border; @apply border-border;
+1 -7
View File
@@ -5,7 +5,6 @@ import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import Link from "next/link"; import Link from "next/link";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useMediaQuery } from "usehooks-ts"; import { useMediaQuery } from "usehooks-ts";
@@ -25,8 +24,6 @@ export function NavbarMenu({
const pathnames = usePathname(); const pathnames = usePathname();
const pathname = `/${pathnames.split("/")[1]}`; const pathname = `/${pathnames.split("/")[1]}`;
const router = useRouter();
const pages = [ const pages = [
{ {
name: "Workflows", name: "Workflows",
@@ -59,11 +56,8 @@ export function NavbarMenu({
<TabsTrigger <TabsTrigger
key={page.name} key={page.name}
value={page.path} value={page.path}
onClick={() => {
router.push(page.path);
}}
> >
{page.name} <Link href={page.path}>{page.name}</Link>
</TabsTrigger> </TabsTrigger>
))} ))}
</TabsList> </TabsList>
+24 -25
View File
@@ -60,7 +60,6 @@ import { callServerPromise } from "./callServerPromise";
import fetcher from "./fetcher"; import fetcher from "./fetcher";
import { ButtonAction } from "@/components/ButtonActionLoader"; import { ButtonAction } from "@/components/ButtonActionLoader";
import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine"; import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine";
import { VisualizeImagesGrid } from "@/components/VisualizeImagesGrid";
export function VersionSelect({ export function VersionSelect({
workflow, workflow,
@@ -170,21 +169,19 @@ export function useSelectedMachine(
} }
type PublicRunStore = { type PublicRunStore = {
image: { image: string;
url: string;
}[] | null;
loading: boolean; loading: boolean;
runId: string; runId: string;
status: string; status: string;
setImage: (image: { url: string; }[]) => void; setImage: (image: string) => void;
setLoading: (loading: boolean) => void; setLoading: (loading: boolean) => void;
setRunId: (runId: string) => void; setRunId: (runId: string) => void;
setStatus: (status: string) => void; setStatus: (status: string) => void;
}; };
export const publicRunStore = create<PublicRunStore>((set) => ({ export const publicRunStore = create<PublicRunStore>((set) => ({
image: null, image: "",
loading: false, loading: false,
runId: "", runId: "",
status: "", status: "",
@@ -208,10 +205,7 @@ export function PublicRunOutputs(props: {
console.log(res?.status); console.log(res?.status);
if (res) setStatus(res.status); if (res) setStatus(res.status);
if (res && res.status === "success") { if (res && res.status === "success") {
const imageURLs = res.outputs[0]?.data.images.map((item: { url: string; }) => { setImage(res.outputs[0]?.data.images[0].url);
return { url: item.url };
});
setImage(imageURLs);
setLoading(false); setLoading(false);
clearInterval(interval); clearInterval(interval);
} }
@@ -220,25 +214,30 @@ export function PublicRunOutputs(props: {
return () => clearInterval(interval); return () => clearInterval(interval);
}, [runId]); }, [runId]);
if (loading) { return (
return ( <div className="border border-gray-200 w-full square h-[400px] rounded-lg relative">
<div className="border border-gray-200 w-full h-[400px] square rounded-lg relative p-4 "> {!loading && !image && props.preview && props.preview.length > 0 && (
<>
<img
className="w-full h-full object-contain"
src={props.preview[0]?.url}
alt="Generated image"
/>
</>
)}
{!loading && image && (
<img
className="w-full h-full object-contain"
src={image}
alt="Generated image"
/>
)}
{loading && (
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2"> <div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2">
{status} <LoadingIcon /> {status} <LoadingIcon />
</div> </div>
<Skeleton className="w-full h-full" />
</div>
);
}
return (
<div className="border border-gray-200 w-full min-h-[400px] square rounded-lg relative p-4 ">
{!image && props.preview && props.preview.length > 0 &&
<VisualizeImagesGrid images={props.preview} />
}
{image && (
<VisualizeImagesGrid images={image} />
)} )}
{loading && <Skeleton className="w-full h-full" />}
</div> </div>
); );
} }
@@ -1,42 +0,0 @@
type imagesType = {
url: string;
width?: number;
height?: number;
};
type VisualizeImagesGridProps = {
images: imagesType[];
layout?: 'justify-between' | 'justify-center' | 'justify-start' | 'justify-end';
};
export function VisualizeImagesGrid({ images, layout }: VisualizeImagesGridProps) {
return (
<div className={`flex gap-4 flex-wrap ${layout || 'justify-center'}`}>
<>
{images && images.length > 0 &&
images.map(item => {
if (!item) {
return;
}
if (item?.url.endsWith(".mp4") || item?.url.endsWith(".webm")) {
return (
<video key={item?.url} controls autoPlay className="rounded-xl" style={{ maxHeight: item.height || 370, maxWidth: item.width || "auto" }}>
<source src={item?.url} type="video/mp4" />
<source src={item?.url} type="video/webm" />
Your browser does not support the video tag.
</video>
);
}
return <img
key={item?.url}
className="object-contain overflow-hidden rounded-xl"
src={item?.url}
alt="Generated image"
style={{ maxHeight: item.height || 370, maxWidth: item.width || "auto" }}
/>;
})
}
</>
</div>
);
}