From 6ba59f0cbf9dd3fe2daf12368bc5af234431e738 Mon Sep 17 00:00:00 2001 From: KarrixLee Date: Tue, 20 May 2025 17:41:57 +0800 Subject: [PATCH 1/4] feat: Add download functionality to FileURLRender and SharePageComponent --- src/components/run/SharePageComponent.tsx | 2 + src/components/workflows/OutputRender.tsx | 79 ++++++------------- .../workflows/WorkflowComponent.tsx | 1 - 3 files changed, 27 insertions(+), 55 deletions(-) diff --git a/src/components/run/SharePageComponent.tsx b/src/components/run/SharePageComponent.tsx index 741c4f89..59ba2772 100644 --- a/src/components/run/SharePageComponent.tsx +++ b/src/components/run/SharePageComponent.tsx @@ -603,6 +603,7 @@ function RunDisplay({ runId }: { runId?: string }) { imgClasses="max-h-[60vh] object-contain shadow-md max-w-full" lazyLoading={false} isMainView={true} + canDownload={true} /> ) : ( @@ -620,6 +621,7 @@ function RunDisplay({ runId }: { runId?: string }) { columns={totalUrlCount > 4 ? 3 : 2} displayCount={totalUrlCount > 9 ? 9 : totalUrlCount} isMainView={totalUrlCount === 1} + canDownload={true} /> ); diff --git a/src/components/workflows/OutputRender.tsx b/src/components/workflows/OutputRender.tsx index b62e2919..e239eb72 100644 --- a/src/components/workflows/OutputRender.tsx +++ b/src/components/workflows/OutputRender.tsx @@ -91,6 +91,7 @@ type fileURLRenderProps = { isMainView?: boolean; canFullScreen?: boolean; isSmallView?: boolean; + canDownload?: boolean; }; function _FileURLRender({ @@ -357,7 +358,27 @@ export function FileURLRender(props: fileURLRenderProps) { ) : ( - <_FileURLRender {...props} /> +
+ <_FileURLRender {...props} /> + {props.canDownload && ( +
+ +
+ )} +
)} ); @@ -390,8 +411,6 @@ function FileURLRenderMulti({ columns?: number; isMainView?: boolean; }) { - const [openOnIndex, setOpenOnIndex] = useState(null); - if (!canExpandToView) { if (columns > 1 && urls.length > 1) { return ( @@ -402,6 +421,7 @@ function FileURLRenderMulti({ url={url.url} imgClasses={imgClasses} isMainView={isMainView} + canDownload={canDownload} /> ))} @@ -416,6 +436,7 @@ function FileURLRenderMulti({ url={url.url} imgClasses={imgClasses} isMainView={isMainView} + canDownload={canDownload} /> ))} @@ -425,47 +446,6 @@ function FileURLRenderMulti({ // Render the image list directly instead of using a nested component return ( <> - setOpenOnIndex(null)} - > - - - - - {urls.length === 1 && ( - - )} - {urls.length > 1 && ( - - - {urls.map((image, index) => ( - - - - ))} - - <> - - - - - )} - - - {columns > 1 ? (
{urls.map((urlImage, i) => { @@ -475,9 +455,6 @@ function FileURLRenderMulti({ urlImage={urlImage} imgClasses={imgClasses} lazyLoading={lazyLoading} - onClick={() => { - setOpenOnIndex(i); - }} canDownload={canDownload} /> ); @@ -491,9 +468,6 @@ function FileURLRenderMulti({ urlImage={urlImage} imgClasses={imgClasses} lazyLoading={lazyLoading} - onClick={() => { - setOpenOnIndex(i); - }} canDownload={canDownload} /> ))} @@ -507,13 +481,11 @@ function MediaDisplay({ urlImage, imgClasses, lazyLoading, - onClick, - canDownload, + canDownload = false, }: { urlImage: any; imgClasses: string; lazyLoading: boolean; - onClick: () => void; canDownload: boolean; }) { const [moveDialogOpen, setMoveDialogOpen] = useState(false); @@ -617,7 +589,6 @@ function MediaDisplay({ fileName: urlImage.filename, }); }} - disabled={!canDownload} >
Download diff --git a/src/components/workflows/WorkflowComponent.tsx b/src/components/workflows/WorkflowComponent.tsx index 4348467e..31a62c73 100644 --- a/src/components/workflows/WorkflowComponent.tsx +++ b/src/components/workflows/WorkflowComponent.tsx @@ -294,7 +294,6 @@ export function RunDetails(props: { run={run as any} imgClasses="max-w-[230px] w-full h-[230px] object-cover object-center rounded-[8px]" canExpandToView={true} - canDownload={true} columns={2} /> From a8b4fbcdadd165a6c6974e644aa83b720f0e1545 Mon Sep 17 00:00:00 2001 From: bennykok Date: Wed, 21 May 2025 00:19:10 +0800 Subject: [PATCH 2/4] fix: mobile navbar layout --- src/components/user-filter-select.tsx | 2 +- src/routes/__root.tsx | 13 +- src/routes/pricing.tsx | 187 +++++++++++++++++--------- 3 files changed, 134 insertions(+), 68 deletions(-) diff --git a/src/components/user-filter-select.tsx b/src/components/user-filter-select.tsx index 0df48f99..da9764a3 100644 --- a/src/components/user-filter-select.tsx +++ b/src/components/user-filter-select.tsx @@ -190,7 +190,7 @@ export function UserFilterSelect({ onFilterChange }: UserFilterSelectProps) { {selectedUsers.length} ) : ( - "Filter by user" + "Filter" )} diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 074d8ef2..792d74fb 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -1,4 +1,5 @@ import { + Link, Outlet, createRootRouteWithContext, redirect, @@ -31,6 +32,7 @@ import { } from "@clerk/clerk-react"; import { Toaster } from "sonner"; import { Providers, queryClient } from "../lib/providers"; +import { Icon } from "@/components/icon-word"; export type RootRouteContext = { auth?: ReturnType; @@ -124,11 +126,18 @@ function RootComponent() { )} -
+ {/* Mobile navbar */} +
+ + {/* Logo */} + + + +
+
- {!isAuthPage && ( diff --git a/src/routes/pricing.tsx b/src/routes/pricing.tsx index 7c16d4e4..57bdf2e3 100644 --- a/src/routes/pricing.tsx +++ b/src/routes/pricing.tsx @@ -234,22 +234,25 @@ function PricingTier({ if (tierName === "Business" && (!showCreatorTier || !showDeploymentTier)) { return sections[0].features.filter((feature) => { const value = feature.tiers[tierName as keyof TierFeature]; - if ((typeof value === "boolean" && value) || (value !== undefined && value !== null)) { + if ( + (typeof value === "boolean" && value) || + (value !== undefined && value !== null) + ) { return true; } - + if (!showCreatorTier && feature.tiers.Creator) { return true; } - + if (!showDeploymentTier && feature.tiers.Deployment) { return true; } - + return false; }); } - + return sections[0].features.filter((feature) => { const value = feature.tiers[tierName as keyof TierFeature]; if (typeof value === "boolean") return value; @@ -261,7 +264,9 @@ function PricingTier({ if (tier.name === "Free") return null; if (tier.name === "Creator") return "Includes everything in Free"; if (tier.name === "Deployment") { - return showCreatorTier ? "Includes everything in Creator" : "Includes everything in Free"; + return showCreatorTier + ? "Includes everything in Creator" + : "Includes everything in Free"; } if (tier.name === "Business") { if (showDeploymentTier) return "Includes everything in Deployment"; @@ -403,25 +408,37 @@ function PricingTier({ tier.name === "Creator" ? feature.tiers.Basic : tier.name === "Deployment" - ? (showCreatorTier ? feature.tiers.Creator : feature.tiers.Basic) + ? showCreatorTier + ? feature.tiers.Creator + : feature.tiers.Basic : tier.name === "Business" - ? (showDeploymentTier - ? feature.tiers.Deployment - : showCreatorTier - ? feature.tiers.Creator - : feature.tiers.Basic) + ? showDeploymentTier + ? feature.tiers.Deployment + : showCreatorTier + ? feature.tiers.Creator + : feature.tiers.Basic : null; - + if (tier.name === "Business") { - if (!showDeploymentTier && feature.tiers.Deployment && - JSON.stringify(feature.tiers.Deployment) !== JSON.stringify(value) && - JSON.stringify(feature.tiers.Deployment) !== JSON.stringify(prevTierValue)) { + if ( + !showDeploymentTier && + feature.tiers.Deployment && + JSON.stringify(feature.tiers.Deployment) !== + JSON.stringify(value) && + JSON.stringify(feature.tiers.Deployment) !== + JSON.stringify(prevTierValue) + ) { return true; } - - if (!showCreatorTier && feature.tiers.Creator && - JSON.stringify(feature.tiers.Creator) !== JSON.stringify(value) && - JSON.stringify(feature.tiers.Creator) !== JSON.stringify(prevTierValue)) { + + if ( + !showCreatorTier && + feature.tiers.Creator && + JSON.stringify(feature.tiers.Creator) !== + JSON.stringify(value) && + JSON.stringify(feature.tiers.Creator) !== + JSON.stringify(prevTierValue) + ) { return true; } } @@ -644,25 +661,31 @@ export function PricingPage() { // Determine user's current plan const userPlans = _sub?.plans?.plans || []; - const isOnCreatorPlan = userPlans.some((plan: string) => plan.startsWith('creator')); - const isOnDeploymentPlan = userPlans.some((plan: string) => plan.startsWith('deployment')); - const isOnBusinessPlan = userPlans.some((plan: string) => plan.startsWith('business')); - + const isOnCreatorPlan = userPlans.some((plan: string) => + plan.startsWith("creator"), + ); + const isOnDeploymentPlan = userPlans.some((plan: string) => + plan.startsWith("deployment"), + ); + const isOnBusinessPlan = userPlans.some((plan: string) => + plan.startsWith("business"), + ); + const filteredTiers = tiers.filter((tier) => { - if (tier.id === 'free') return true; - - if (tier.id === 'business') return true; - - if (tier.id === 'large_enterprise') return true; - - if (tier.id === 'creator' || tier.id === 'deployment') { + if (tier.id === "free") return true; + + if (tier.id === "business") return true; + + if (tier.id === "large_enterprise") return true; + + if (tier.id === "creator" || tier.id === "deployment") { if (isOnCreatorPlan || isOnDeploymentPlan) return true; - + if (userPlans.length === 0 || isOnBusinessPlan) return false; - + return true; } - + return true; }); @@ -670,7 +693,7 @@ export function PricingPage() { return (
-
+
{/* Header */}

@@ -788,52 +811,72 @@ export function PricingPage() { {/* Free Tier */}
- {filteredTiers.find(tier => tier.id === 'free') && ( + {filteredTiers.find((tier) => tier.id === "free") && ( tier.id === 'free')!} + tier={filteredTiers.find((tier) => tier.id === "free")!} isLoading={isLoading} plans={_sub?.plans?.plans ?? []} className="rounded-t-sm border bg-gradient-to-bl from-gray-50/10 via-gray-50/80 to-gray-100" isYearly={isYearly} - showCreatorTier={filteredTiers.some(tier => tier.id === 'creator')} - showDeploymentTier={filteredTiers.some(tier => tier.id === 'deployment')} + showCreatorTier={filteredTiers.some( + (tier) => tier.id === "creator", + )} + showDeploymentTier={filteredTiers.some( + (tier) => tier.id === "deployment", + )} /> )}
{/* Creator and Deployment Tiers */} - {(filteredTiers.some(tier => tier.id === 'creator') || - filteredTiers.some(tier => tier.id === 'deployment')) && ( -
tier.id === 'creator') && - filteredTiers.some(tier => tier.id === 'deployment')) - ? "grid-cols-1 lg:grid-cols-2" - : "grid-cols-1" - )}> - {filteredTiers.find(tier => tier.id === 'creator') && ( + {(filteredTiers.some((tier) => tier.id === "creator") || + filteredTiers.some((tier) => tier.id === "deployment")) && ( +
tier.id === "creator") && + filteredTiers.some((tier) => tier.id === "deployment") + ? "grid-cols-1 lg:grid-cols-2" + : "grid-cols-1", + )} + > + {filteredTiers.find((tier) => tier.id === "creator") && ( tier.id === 'creator')!} + tier={ + filteredTiers.find((tier) => tier.id === "creator")! + } isLoading={isLoading} plans={_sub?.plans?.plans ?? []} className={cn( "bg-gradient-to-bl from-amber-50/10 via-amber-50/80 to-amber-100", - filteredTiers.some(tier => tier.id === 'deployment') ? "lg:border-r" : "" + filteredTiers.some((tier) => tier.id === "deployment") + ? "lg:border-r" + : "", )} isYearly={isYearly} - showCreatorTier={filteredTiers.some(tier => tier.id === 'creator')} - showDeploymentTier={filteredTiers.some(tier => tier.id === 'deployment')} + showCreatorTier={filteredTiers.some( + (tier) => tier.id === "creator", + )} + showDeploymentTier={filteredTiers.some( + (tier) => tier.id === "deployment", + )} /> )} - {filteredTiers.find(tier => tier.id === 'deployment') && ( + {filteredTiers.find((tier) => tier.id === "deployment") && ( tier.id === 'deployment')!} + tier={ + filteredTiers.find((tier) => tier.id === "deployment")! + } isLoading={isLoading} plans={_sub?.plans?.plans ?? []} className="bg-gradient-to-bl from-blue-50/10 via-blue-50/80 to-blue-100" isYearly={isYearly} - showCreatorTier={filteredTiers.some(tier => tier.id === 'creator')} - showDeploymentTier={filteredTiers.some(tier => tier.id === 'deployment')} + showCreatorTier={filteredTiers.some( + (tier) => tier.id === "creator", + )} + showDeploymentTier={filteredTiers.some( + (tier) => tier.id === "deployment", + )} /> )}
@@ -841,30 +884,44 @@ export function PricingPage() { {/* Business Tier */}
- {filteredTiers.find(tier => tier.id === 'business') && ( + {filteredTiers.find((tier) => tier.id === "business") && ( tier.id === 'business')!} + tier={filteredTiers.find((tier) => tier.id === "business")!} isLoading={isLoading} plans={_sub?.plans?.plans ?? []} className="border border-t-0 bg-gradient-to-bl from-purple-50/10 via-purple-50/80 to-purple-100" isYearly={isYearly} - showCreatorTier={filteredTiers.some(tier => tier.id === 'creator')} - showDeploymentTier={filteredTiers.some(tier => tier.id === 'deployment')} + showCreatorTier={filteredTiers.some( + (tier) => tier.id === "creator", + )} + showDeploymentTier={filteredTiers.some( + (tier) => tier.id === "deployment", + )} /> )}
{/* Enterprise Tier */}
- {filteredTiers.find(tier => tier.id === 'large_enterprise') && ( + {filteredTiers.find( + (tier) => tier.id === "large_enterprise", + ) && ( tier.id === 'large_enterprise')!} + tier={ + filteredTiers.find( + (tier) => tier.id === "large_enterprise", + )! + } isLoading={isLoading} plans={_sub?.plans?.plans ?? []} className="overflow-hidden rounded-b-sm border border-t-0 bg-gradient-to-bl from-indigo-50/10 via-indigo-50/80 to-indigo-100" isYearly={isYearly} - showCreatorTier={filteredTiers.some(tier => tier.id === 'creator')} - showDeploymentTier={filteredTiers.some(tier => tier.id === 'deployment')} + showCreatorTier={filteredTiers.some( + (tier) => tier.id === "creator", + )} + showDeploymentTier={filteredTiers.some( + (tier) => tier.id === "deployment", + )} /> )}
From e0491fe025626c77e115c559929dc94a976e878f Mon Sep 17 00:00:00 2001 From: KarrixLee Date: Wed, 21 May 2025 04:17:08 +0800 Subject: [PATCH 3/4] feat: Update workflow import component to handle environment data and enhance template selection UI --- src/components/onboarding/workflow-import.tsx | 86 +- .../workspace/workspace-buttons.tsx | 1 - src/utils/default-workflow.ts | 3188 +++++++++++++---- 3 files changed, 2514 insertions(+), 761 deletions(-) diff --git a/src/components/onboarding/workflow-import.tsx b/src/components/onboarding/workflow-import.tsx index 618c5e23..63ed8088 100644 --- a/src/components/onboarding/workflow-import.tsx +++ b/src/components/onboarding/workflow-import.tsx @@ -30,12 +30,12 @@ import { api } from "@/lib/api"; import { cn } from "@/lib/utils"; import { comfyui_hash } from "@/utils/comfydeploy-hash"; import { defaultWorkflowTemplates } from "@/utils/default-workflow"; -import { useNavigate, useSearch } from "@tanstack/react-router"; -import { CheckCircle2, Circle, CircleCheckBig } from "lucide-react"; -import { useSearchParams } from "next/navigation"; +import { useNavigate } from "@tanstack/react-router"; +import { CheckCircle2, Circle, CircleCheckBig, Lightbulb } from "lucide-react"; import { useQueryState } from "nuqs"; import { useCallback, useEffect, useRef, useState } from "react"; import { toast } from "sonner"; +import { FileURLRender } from "../workflows/OutputRender"; // Add these interfaces export interface StepValidation { @@ -647,12 +647,37 @@ function DefaultOption({ ); if (selectedTemplate && validation.importOption === "default") { - setValidation({ + const updatedValidation = { ...validation, workflowJson: selectedTemplate.workflowJson, workflowApi: selectedTemplate.workflowApi, importJson: "", - }); + hasEnvironment: selectedTemplate.hasEnvironment || false, + }; + + // If the template has environment data, include those properties + if (selectedTemplate.hasEnvironment) { + try { + const workflowData = JSON.parse(selectedTemplate.workflowJson); + const environment = workflowData.environment; + + if (environment) { + Object.assign(updatedValidation, { + docker_command_steps: environment.docker_command_steps, + gpuType: environment.gpu, + comfyUiHash: environment.comfyui_version, + install_custom_node_with_gpu: + environment.install_custom_node_with_gpu, + base_docker_image: environment.base_docker_image, + python_version: environment.python_version, + }); + } + } catch (error) { + console.error("Error parsing workflow JSON:", error); + } + } + + setValidation(updatedValidation); } }, [workflowSelected, validation.importOption]); @@ -668,40 +693,45 @@ function DefaultOption({ Select a workflow as your starting point.{" "} -
+
{defaultWorkflowTemplates.map((template, index) => (
- - - - {sub && ( - -
- {sub?.features.currentWorkflowCount}/ - {sub?.features.workflowLimit} -
-
- )} -
- -

- Current workflows: {sub?.features.currentWorkflowCount} / Max:{" "} - {sub?.features.workflowLimit} -

-
-
-
+ + + {sub && ( + +
+ {sub?.features.currentWorkflowCount}/ + {sub?.features.workflowLimit} +
+
+ )} +
+ +

+ Current workflows: {sub?.features.currentWorkflowCount} / Max:{" "} + {sub?.features.workflowLimit} +

+
+
diff --git a/src/routes/workflows/index.lazy.tsx b/src/routes/workflows/index.lazy.tsx index 50712c77..04cafa52 100644 --- a/src/routes/workflows/index.lazy.tsx +++ b/src/routes/workflows/index.lazy.tsx @@ -51,7 +51,9 @@ function RouteComponent() { }} disabled={{ disabled: sub?.features.workflowLimited || !isAdminOrMember, - disabledText: "Workflows Limited Exceeded. ", + disabledText: sub?.features.workflowLimited + ? "Workflow Limit Exceeded." + : "Insufficient permissions to create workflows.", }} /> )}