From 8ff5c95168faee89b3622ca026a6bb44e53ed750 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Fri, 23 Jan 2026 14:19:38 -0800 Subject: [PATCH 1/4] WIP --- .../_dashboard/project/$projectId/page.tsx | 420 ++++++++++++++++++ .../main/components/StartView/index.tsx | 389 +++++++++------- .../SidebarDropZone/SidebarDropZone.tsx | 26 +- 3 files changed, 664 insertions(+), 171 deletions(-) create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx new file mode 100644 index 00000000000..ab4dd42f28f --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx @@ -0,0 +1,420 @@ +import { Button } from "@superset/ui/button"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@superset/ui/collapsible"; +import { + Command, + CommandEmpty, + CommandInput, + CommandItem, + CommandList, +} from "@superset/ui/command"; +import { Input } from "@superset/ui/input"; +import { Popover, PopoverContent, PopoverTrigger } from "@superset/ui/popover"; +import { toast } from "@superset/ui/sonner"; +import { createFileRoute, notFound, useNavigate } from "@tanstack/react-router"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { GoGitBranch } from "react-icons/go"; +import { HiCheck, HiChevronDown, HiChevronUpDown } from "react-icons/hi2"; +import { LuArrowRight } from "react-icons/lu"; +import { electronTrpc } from "renderer/lib/electron-trpc"; +import { formatRelativeTime } from "renderer/lib/formatRelativeTime"; +import { electronTrpcClient as trpcClient } from "renderer/lib/trpc-client"; +import { + useCreateBranchWorkspace, + useCreateWorkspace, +} from "renderer/react-query/workspaces"; +import { NotFound } from "renderer/routes/not-found"; + +export const Route = createFileRoute( + "/_authenticated/_dashboard/project/$projectId/", +)({ + component: ProjectPage, + notFoundComponent: NotFound, + loader: async ({ params, context }) => { + const queryKey = [ + ["projects", "get"], + { input: { id: params.projectId }, type: "query" }, + ]; + + try { + await context.queryClient.ensureQueryData({ + queryKey, + queryFn: () => trpcClient.projects.get.query({ id: params.projectId }), + }); + } catch (error) { + if (error instanceof Error && error.message.includes("not found")) { + throw notFound(); + } + throw error; + } + }, +}); + +function generateBranchFromTitle(title: string): string { + if (!title.trim()) return ""; + + return title + .toLowerCase() + .trim() + .replace(/[^a-z0-9\s-]/g, "") + .replace(/\s+/g, "-") + .replace(/-+/g, "-") + .replace(/^-|-$/g, "") + .slice(0, 50); +} + +function ProjectPage() { + const { projectId } = Route.useParams(); + const navigate = useNavigate(); + + const { data: project } = electronTrpc.projects.get.useQuery({ + id: projectId, + }); + const { data: workspacesData } = + electronTrpc.workspaces.getAllGrouped.useQuery(); + const { + data: branchData, + isLoading: isBranchesLoading, + isError: isBranchesError, + } = electronTrpc.projects.getBranches.useQuery( + { projectId }, + { enabled: !!projectId }, + ); + + const createWorkspace = useCreateWorkspace(); + const createBranchWorkspace = useCreateBranchWorkspace(); + + const [title, setTitle] = useState(""); + const [branchName, setBranchName] = useState(""); + const [branchNameEdited, setBranchNameEdited] = useState(false); + const [baseBranch, setBaseBranch] = useState(null); + const [baseBranchOpen, setBaseBranchOpen] = useState(false); + const [branchSearch, setBranchSearch] = useState(""); + const [showAdvanced, setShowAdvanced] = useState(false); + const titleInputRef = useRef(null); + + // Get existing workspaces for this project + const projectWorkspaces = useMemo(() => { + if (!workspacesData) return []; + const projectGroup = workspacesData.find((g) => g.project.id === projectId); + return projectGroup?.workspaces ?? []; + }, [workspacesData, projectId]); + + // Filter branches based on search + const filteredBranches = useMemo(() => { + if (!branchData?.branches) return []; + if (!branchSearch) return branchData.branches; + const searchLower = branchSearch.toLowerCase(); + return branchData.branches.filter((b) => + b.name.toLowerCase().includes(searchLower), + ); + }, [branchData?.branches, branchSearch]); + + // Effective base branch + const effectiveBaseBranch = baseBranch ?? branchData?.defaultBranch ?? null; + + // Auto-generate branch name from title + useEffect(() => { + if (!branchNameEdited) { + setBranchName(generateBranchFromTitle(title)); + } + }, [title, branchNameEdited]); + + // Focus title input on mount + useEffect(() => { + const timer = setTimeout(() => { + titleInputRef.current?.focus(); + }, 100); + return () => clearTimeout(timer); + }, []); + + const handleBranchNameChange = (value: string) => { + setBranchName(value); + setBranchNameEdited(true); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey && !createWorkspace.isPending) { + e.preventDefault(); + handleCreateWorkspace(); + } + }; + + const handleCreateWorkspace = async () => { + const workspaceName = title.trim() || undefined; + const customBranchName = branchName.trim() || undefined; + + try { + await createWorkspace.mutateAsync({ + projectId, + name: workspaceName, + branchName: customBranchName, + baseBranch: effectiveBaseBranch || undefined, + }); + + toast.success("Workspace created", { + description: "Setting up in the background...", + }); + } catch (err) { + toast.error( + err instanceof Error ? err.message : "Failed to create workspace", + ); + } + }; + + const handleOpenMainBranch = () => { + createBranchWorkspace.mutate( + { projectId }, + { + onError: (err) => { + toast.error(err.message || "Failed to open main branch"); + }, + }, + ); + }; + + if (!project) { + return null; + } + + return ( +
+
+ {/* biome-ignore lint/a11y/noStaticElementInteractions: Form container handles Enter key for submission */} +
+ {/* Project Header */} +
+

+ {project.name} +

+

+ {branchData?.defaultBranch ?? "main"} •{" "} + {branchData?.branches?.length ?? 0} branches +

+
+ + {/* Value Prop */} +
+

+ Superset keeps your work organized. +

+

+ Each workspace is an isolated copy of your repo. Switch between + tasks without stashing, losing context, or breaking your build. +

+
+ + {/* Workspace Creation Form */} +
+
+ + setTitle(e.target.value)} + /> +
+ + {title && !showAdvanced && ( +

+ + + {branchName || generateBranchFromTitle(title)} + + + from {effectiveBaseBranch} + +

+ )} + + + + + Advanced options + + +
+ + handleBranchNameChange(e.target.value)} + /> +
+ +
+ + Base branch + + {isBranchesError ? ( +
+ Failed to load branches +
+ ) : ( + + + + + e.stopPropagation()} + > + + + + No branches found + {filteredBranches.map((branch) => ( + { + setBaseBranch(branch.name); + setBaseBranchOpen(false); + setBranchSearch(""); + }} + className="flex items-center justify-between" + > + + + + {branch.name} + + {branch.name === + branchData?.defaultBranch && ( + + default + + )} + + + {branch.lastCommitDate > 0 && ( + + {formatRelativeTime( + branch.lastCommitDate, + )} + + )} + {effectiveBaseBranch === branch.name && ( + + )} + + + ))} + + + + + )} +
+
+
+ + +
+ + {/* Open main branch link */} +
+ +
+ + {/* Existing Workspaces */} + {projectWorkspaces.length > 0 && ( +
+

+ Existing Workspaces +

+
+ {projectWorkspaces.map((workspace) => ( + + ))} +
+
+ )} +
+
+
+ ); +} diff --git a/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx b/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx index f3ccd2d9984..8576e6e12e7 100644 --- a/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx +++ b/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx @@ -1,30 +1,47 @@ -import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; -import { useState } from "react"; -import { HiExclamationTriangle } from "react-icons/hi2"; -import { LuChevronUp, LuFolderGit, LuFolderOpen, LuX } from "react-icons/lu"; -import { electronTrpc } from "renderer/lib/electron-trpc"; -import { formatPathWithProject } from "renderer/lib/formatPath"; -import { useOpenNew } from "renderer/react-query/projects"; -import { useCreateBranchWorkspace } from "renderer/react-query/workspaces"; -import { ActionCard } from "./ActionCard"; +import { cn } from "@superset/ui/utils"; +import { useNavigate } from "@tanstack/react-router"; +import { AnimatePresence, motion } from "framer-motion"; +import { useCallback, useEffect, useState } from "react"; +import { LuFolderOpen, LuLoader, LuX } from "react-icons/lu"; +import { useOpenFromPath, useOpenNew } from "renderer/react-query/projects"; import { CloneRepoDialog } from "./CloneRepoDialog"; import { InitGitDialog } from "./InitGitDialog"; import { StartTopBar } from "./StartTopBar"; export function StartView() { - const { data: recentProjects = [] } = - electronTrpc.projects.getRecents.useQuery(); - const { data: homeDir } = electronTrpc.window.getHomeDir.useQuery(); + const navigate = useNavigate(); const openNew = useOpenNew(); - const createBranchWorkspace = useCreateBranchWorkspace(); + const openFromPath = useOpenFromPath(); const [error, setError] = useState(null); const [isCloneDialogOpen, setIsCloneDialogOpen] = useState(false); const [initGitDialog, setInitGitDialog] = useState<{ isOpen: boolean; selectedPath: string; }>({ isOpen: false, selectedPath: "" }); - const [showAllProjects, setShowAllProjects] = useState(false); - const [visibleCount, setVisibleCount] = useState(50); + const [isDragOver, setIsDragOver] = useState(false); + + const isLoading = openNew.isPending || openFromPath.isPending; + + // Auto-dismiss error after 5 seconds + useEffect(() => { + if (!error) return; + const timer = setTimeout(() => setError(null), 5000); + return () => clearTimeout(timer); + }, [error]); + + // Clear drag state when drag ends anywhere + useEffect(() => { + const handleWindowDragEnd = () => setIsDragOver(false); + const handleWindowDrop = () => setIsDragOver(false); + + window.addEventListener("dragend", handleWindowDragEnd); + window.addEventListener("drop", handleWindowDrop); + + return () => { + window.removeEventListener("dragend", handleWindowDragEnd); + window.removeEventListener("drop", handleWindowDrop); + }; + }, []); const handleOpenProject = () => { setError(null); @@ -40,7 +57,6 @@ export function StartView() { } if ("needsGitInit" in result) { - // Show dialog to offer git initialization setInitGitDialog({ isOpen: true, selectedPath: result.selectedPath, @@ -48,8 +64,13 @@ export function StartView() { return; } - // Create a main workspace on the current branch - createBranchWorkspace.mutate({ projectId: result.project.id }); + // Navigate to project view + if ("project" in result && result.project) { + navigate({ + to: "/project/$projectId", + params: { projectId: result.project.id }, + }); + } }, onError: (err) => { setError(err.message || "Failed to open project"); @@ -57,40 +78,118 @@ export function StartView() { }); }; - const handleOpenRecentProject = (projectId: string) => { - setError(null); - // Create/activate main workspace on current branch - createBranchWorkspace.mutate( - { projectId }, - { - onError: (err) => { - setError(err.message || "Failed to open workspace"); - }, - }, - ); - }; + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + + if (e.dataTransfer.types.includes("Files")) { + setIsDragOver(true); + e.dataTransfer.dropEffect = "copy"; + } + }, []); + + const handleDragLeave = useCallback((e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + + const rect = e.currentTarget.getBoundingClientRect(); + const { clientX, clientY } = e; + + if ( + clientX < rect.left || + clientX > rect.right || + clientY < rect.top || + clientY > rect.bottom + ) { + setIsDragOver(false); + } + }, []); + + const handleDrop = useCallback( + (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragOver(false); + + if (isLoading) return; + + setError(null); + + const files = Array.from(e.dataTransfer.files); + const firstFile = files[0]; + if (!firstFile) return; + + let filePath: string; + try { + filePath = window.webUtils.getPathForFile(firstFile); + } catch { + setError("Could not get path from dropped item"); + return; + } + + if (!filePath) { + setError("Could not get path from dropped item"); + return; + } + + openFromPath.mutate( + { path: filePath }, + { + onSuccess: (result) => { + if ("canceled" in result && result.canceled) { + return; + } - const hasMoreProjects = recentProjects.length > 5; - const displayedProjects = showAllProjects - ? recentProjects.slice(0, visibleCount) - : recentProjects.slice(0, 5); - const hasMoreToLoad = showAllProjects && recentProjects.length > visibleCount; - const isLoading = openNew.isPending || createBranchWorkspace.isPending; + if ("error" in result) { + setError(result.error); + return; + } + + if ("needsGitInit" in result) { + setInitGitDialog({ + isOpen: true, + selectedPath: result.selectedPath, + }); + return; + } + + // Navigate to project view + if ("project" in result && result.project) { + navigate({ + to: "/project/$projectId", + params: { projectId: result.project.id }, + }); + } + }, + onError: (err) => { + setError(err.message || "Failed to open project"); + }, + }, + ); + }, + [openFromPath, isLoading, navigate], + ); return (
-
-
+ {/* biome-ignore lint/a11y/noStaticElementInteractions: Drop zone for external files */} +
+
{/* Logo */} -
+
@@ -102,128 +201,110 @@ export function StartView() {
+ {/* Drop Zone */} + + + {/* Clone repo link */} + + {/* Error Display */} - {error && ( -
-
-
- -
-

{error}

+ + {error && ( + + {error} -
-
- )} - - {/* Action Cards and Recent Projects Container */} -
- {/* Action Cards */} -
- - - { - setError(null); - setIsCloneDialogOpen(true); - }} - isLoading={isLoading} - /> -
- - {/* Recent Projects */} - {displayedProjects.length > 0 && ( -
-
-
- - Recent projects - - {hasMoreProjects && ( - - )} -
- -
- {displayedProjects.map((project) => { - const pathInfo = formatPathWithProject( - project.mainRepoPath, - project.name, - homeDir, - ); - return ( - - ); - })} - - {hasMoreToLoad && ( - - )} -
-
-
+ )} -
+
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/SidebarDropZone/SidebarDropZone.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/SidebarDropZone/SidebarDropZone.tsx index b194b5bc8a7..74e42cd6e9d 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/SidebarDropZone/SidebarDropZone.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/SidebarDropZone/SidebarDropZone.tsx @@ -1,9 +1,9 @@ import { cn } from "@superset/ui/utils"; +import { useNavigate } from "@tanstack/react-router"; import { AnimatePresence, motion } from "framer-motion"; import { type ReactNode, useCallback, useEffect, useState } from "react"; import { LuFolderPlus, LuLoader, LuX } from "react-icons/lu"; import { useOpenFromPath } from "renderer/react-query/projects"; -import { useCreateBranchWorkspace } from "renderer/react-query/workspaces"; import { InitGitDialog } from "../../StartView/InitGitDialog"; interface SidebarDropZoneProps { @@ -12,6 +12,7 @@ interface SidebarDropZoneProps { } export function SidebarDropZone({ children, className }: SidebarDropZoneProps) { + const navigate = useNavigate(); const [isDragOver, setIsDragOver] = useState(false); const [error, setError] = useState(null); const [initGitDialog, setInitGitDialog] = useState<{ @@ -20,10 +21,8 @@ export function SidebarDropZone({ children, className }: SidebarDropZoneProps) { }>({ isOpen: false, selectedPath: "" }); const openFromPath = useOpenFromPath(); - const createBranchWorkspace = useCreateBranchWorkspace(); - const isProcessing = - openFromPath.isPending || createBranchWorkspace.isPending; + const isProcessing = openFromPath.isPending; // Auto-dismiss error after 5 seconds useEffect(() => { @@ -138,19 +137,12 @@ export function SidebarDropZone({ children, className }: SidebarDropZoneProps) { return; } - // Create a main workspace on the current branch + // Navigate to project view if ("project" in result && result.project) { - createBranchWorkspace.mutate( - { projectId: result.project.id }, - { - onError: (err) => { - setError( - err.message || - "Project added but failed to create workspace", - ); - }, - }, - ); + navigate({ + to: "/project/$projectId", + params: { projectId: result.project.id }, + }); } }, onError: (err) => { @@ -159,7 +151,7 @@ export function SidebarDropZone({ children, className }: SidebarDropZoneProps) { }, ); }, - [openFromPath, createBranchWorkspace, isProcessing], + [openFromPath, isProcessing, navigate], ); return ( From 443e851e15e9c63e722b068fbfc4a3cb6a0c6bd4 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Fri, 23 Jan 2026 15:35:52 -0800 Subject: [PATCH 2/4] WIP --- .../_dashboard/project/$projectId/page.tsx | 420 +++++++----------- .../ProjectSection/ProjectHeader.tsx | 4 +- 2 files changed, 163 insertions(+), 261 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx index ab4dd42f28f..ec8de5e9df6 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx @@ -1,9 +1,5 @@ import { Button } from "@superset/ui/button"; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from "@superset/ui/collapsible"; +import { Collapsible, CollapsibleTrigger } from "@superset/ui/collapsible"; import { Command, CommandEmpty, @@ -14,18 +10,15 @@ import { import { Input } from "@superset/ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "@superset/ui/popover"; import { toast } from "@superset/ui/sonner"; -import { createFileRoute, notFound, useNavigate } from "@tanstack/react-router"; +import { createFileRoute, notFound } from "@tanstack/react-router"; +import { AnimatePresence, motion } from "framer-motion"; import { useEffect, useMemo, useRef, useState } from "react"; import { GoGitBranch } from "react-icons/go"; import { HiCheck, HiChevronDown, HiChevronUpDown } from "react-icons/hi2"; -import { LuArrowRight } from "react-icons/lu"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { formatRelativeTime } from "renderer/lib/formatRelativeTime"; import { electronTrpcClient as trpcClient } from "renderer/lib/trpc-client"; -import { - useCreateBranchWorkspace, - useCreateWorkspace, -} from "renderer/react-query/workspaces"; +import { useCreateWorkspace } from "renderer/react-query/workspaces"; import { NotFound } from "renderer/routes/not-found"; export const Route = createFileRoute( @@ -68,13 +61,10 @@ function generateBranchFromTitle(title: string): string { function ProjectPage() { const { projectId } = Route.useParams(); - const navigate = useNavigate(); const { data: project } = electronTrpc.projects.get.useQuery({ id: projectId, }); - const { data: workspacesData } = - electronTrpc.workspaces.getAllGrouped.useQuery(); const { data: branchData, isLoading: isBranchesLoading, @@ -85,25 +75,14 @@ function ProjectPage() { ); const createWorkspace = useCreateWorkspace(); - const createBranchWorkspace = useCreateBranchWorkspace(); const [title, setTitle] = useState(""); - const [branchName, setBranchName] = useState(""); - const [branchNameEdited, setBranchNameEdited] = useState(false); const [baseBranch, setBaseBranch] = useState(null); const [baseBranchOpen, setBaseBranchOpen] = useState(false); const [branchSearch, setBranchSearch] = useState(""); const [showAdvanced, setShowAdvanced] = useState(false); const titleInputRef = useRef(null); - // Get existing workspaces for this project - const projectWorkspaces = useMemo(() => { - if (!workspacesData) return []; - const projectGroup = workspacesData.find((g) => g.project.id === projectId); - return projectGroup?.workspaces ?? []; - }, [workspacesData, projectId]); - - // Filter branches based on search const filteredBranches = useMemo(() => { if (!branchData?.branches) return []; if (!branchSearch) return branchData.branches; @@ -113,17 +92,8 @@ function ProjectPage() { ); }, [branchData?.branches, branchSearch]); - // Effective base branch const effectiveBaseBranch = baseBranch ?? branchData?.defaultBranch ?? null; - // Auto-generate branch name from title - useEffect(() => { - if (!branchNameEdited) { - setBranchName(generateBranchFromTitle(title)); - } - }, [title, branchNameEdited]); - - // Focus title input on mount useEffect(() => { const timer = setTimeout(() => { titleInputRef.current?.focus(); @@ -131,11 +101,6 @@ function ProjectPage() { return () => clearTimeout(timer); }, []); - const handleBranchNameChange = (value: string) => { - setBranchName(value); - setBranchNameEdited(true); - }; - const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey && !createWorkspace.isPending) { e.preventDefault(); @@ -145,13 +110,11 @@ function ProjectPage() { const handleCreateWorkspace = async () => { const workspaceName = title.trim() || undefined; - const customBranchName = branchName.trim() || undefined; try { await createWorkspace.mutateAsync({ projectId, name: workspaceName, - branchName: customBranchName, baseBranch: effectiveBaseBranch || undefined, }); @@ -165,255 +128,194 @@ function ProjectPage() { } }; - const handleOpenMainBranch = () => { - createBranchWorkspace.mutate( - { projectId }, - { - onError: (err) => { - toast.error(err.message || "Failed to open main branch"); - }, - }, - ); - }; - if (!project) { return null; } return (
-
- {/* biome-ignore lint/a11y/noStaticElementInteractions: Form container handles Enter key for submission */} -
- {/* Project Header */} -
-

- {project.name} +
+ {/* Main content */} +
+ {/* biome-ignore lint/a11y/noStaticElementInteractions: Form container handles Enter key for submission */} +
+ {/* Project context */} +
+ + {project.name} + + · + + {branchData?.defaultBranch ?? "main"} + +
+ + {/* Headline */} +

+ What are you building?

-

- {branchData?.defaultBranch ?? "main"} •{" "} - {branchData?.branches?.length ?? 0} branches -

-
- {/* Value Prop */} -
-

- Superset keeps your work organized. -

-

- Each workspace is an isolated copy of your repo. Switch between - tasks without stashing, losing context, or breaking your build. + {/* Subtext */} +

+ Each workspace is an isolated copy of your codebase. Work on + multiple tasks without conflicts.

-
- {/* Workspace Creation Form */} -
-
- - setTitle(e.target.value)} - /> -
+ {/* Form */} +
+
+ + setTitle(e.target.value)} + /> +
- {title && !showAdvanced && ( -

- +

+ - {branchName || generateBranchFromTitle(title)} + {generateBranchFromTitle(title) || "branch-name"} - + from {effectiveBaseBranch}

- )} - - - - Advanced options - - -
- - handleBranchNameChange(e.target.value)} + + + -
- -
- - Base branch - - {isBranchesError ? ( -
- Failed to load branches -
- ) : ( - + + {showAdvanced && ( + - - - - e.stopPropagation()} - > - - - - No branches found - {filteredBranches.map((branch) => ( - { - setBaseBranch(branch.name); - setBaseBranchOpen(false); - setBranchSearch(""); - }} - className="flex items-center justify-between" +
+ + Change base branch + + {isBranchesError ? ( +
+ Failed to load branches +
+ ) : ( + + + + + e.stopPropagation()} + > + + + + No branches found + {filteredBranches.map((branch) => ( + { + setBaseBranch(branch.name); + setBaseBranchOpen(false); + setBranchSearch(""); + }} + className="flex items-center justify-between" + > + + + + {branch.name} + + {branch.name === + branchData?.defaultBranch && ( + + default + + )} + + + {branch.lastCommitDate > 0 && ( + + {formatRelativeTime( + branch.lastCommitDate, + )} + + )} + {effectiveBaseBranch === branch.name && ( + + )} + + + ))} + + + + + )} +
+
)} -
-
-
- - -
- - {/* Open main branch link */} -
- -
+ + - {/* Existing Workspaces */} - {projectWorkspaces.length > 0 && ( -
-

- Existing Workspaces -

-
- {projectWorkspaces.map((workspace) => ( - - ))} -
+
- )} +
+

); diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx index 16d34c523f3..8c1a0321022 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx @@ -218,7 +218,7 @@ export function ProjectHeader({ disabled={closeProject.isPending} className="text-destructive focus:text-destructive" > - + {closeProject.isPending ? "Closing..." : "Close Project"} @@ -316,7 +316,7 @@ export function ProjectHeader({ disabled={closeProject.isPending} className="text-destructive focus:text-destructive" > - + {closeProject.isPending ? "Closing..." : "Close Project"} From 3cca27e9a23dd07dc7514eaa68064eb7979248f3 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Fri, 23 Jan 2026 18:00:07 -0800 Subject: [PATCH 3/4] WIP clean up tables --- .../_dashboard/project/$projectId/page.tsx | 4 +- .../main/components/StartView/StartTopBar.tsx | 23 +-- .../main/components/StartView/index.tsx | 140 +++++++----------- .../ProjectSection/ProjectHeader.tsx | 10 +- .../WorkspaceSidebar/WorkspaceSidebar.tsx | 2 +- 5 files changed, 72 insertions(+), 107 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx index ec8de5e9df6..e0393c30cd7 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx @@ -287,7 +287,8 @@ function ProjectPage() { )} )} - {effectiveBaseBranch === branch.name && ( + {effectiveBaseBranch === + branch.name && ( )} @@ -315,7 +316,6 @@ function ProjectPage() {
-
); diff --git a/apps/desktop/src/renderer/screens/main/components/StartView/StartTopBar.tsx b/apps/desktop/src/renderer/screens/main/components/StartView/StartTopBar.tsx index aa9d2f76282..02e53f550fc 100644 --- a/apps/desktop/src/renderer/screens/main/components/StartView/StartTopBar.tsx +++ b/apps/desktop/src/renderer/screens/main/components/StartView/StartTopBar.tsx @@ -1,5 +1,4 @@ import { electronTrpc } from "renderer/lib/electron-trpc"; -import { SettingsButton } from "../SettingsButton"; import { WindowControls } from "../TopBar/WindowControls"; export function StartTopBar() { @@ -9,22 +8,12 @@ export function StartTopBar() { const showWindowControls = !isLoading && !isMac; return ( -
-
- {/* Empty space on left for symmetry */} -
-
- {/* Empty middle section - no tabs */} -
-
- - {showWindowControls && } -
+
+ {showWindowControls && ( +
+ +
+ )}
); } diff --git a/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx b/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx index 8576e6e12e7..a3b68d4930f 100644 --- a/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx +++ b/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx @@ -1,9 +1,10 @@ +import { Button } from "@superset/ui/button"; import { cn } from "@superset/ui/utils"; import { useNavigate } from "@tanstack/react-router"; -import { AnimatePresence, motion } from "framer-motion"; import { useCallback, useEffect, useState } from "react"; import { LuFolderOpen, LuLoader, LuX } from "react-icons/lu"; import { useOpenFromPath, useOpenNew } from "renderer/react-query/projects"; +import { SupersetLogo } from "renderer/routes/sign-in/components/SupersetLogo"; import { CloneRepoDialog } from "./CloneRepoDialog"; import { InitGitDialog } from "./InitGitDialog"; import { StartTopBar } from "./StartTopBar"; @@ -171,82 +172,60 @@ export function StartView() { ); return ( -
+
{/* biome-ignore lint/a11y/noStaticElementInteractions: Drop zone for external files */}
-
- {/* Logo */} -
- - Superset - - +
+
+ + Welcome to + +
- {/* Drop Zone */} - + - {/* Clone repo link */} - + - {/* Error Display */} - {error && ( - +
{error} - +
)} -
+
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx index 8c1a0321022..894f9e7e2b3 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx @@ -218,7 +218,10 @@ export function ProjectHeader({ disabled={closeProject.isPending} className="text-destructive focus:text-destructive" > - + {closeProject.isPending ? "Closing..." : "Close Project"} @@ -316,7 +319,10 @@ export function ProjectHeader({ disabled={closeProject.isPending} className="text-destructive focus:text-destructive" > - + {closeProject.isPending ? "Closing..." : "Close Project"} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx index 9da785799f7..087b9ff545a 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -52,7 +52,7 @@ export function WorkspaceSidebar({
No workspaces yet - Add a project or drag a folder here + Add project or drag a Git repo folder here
)} From 060b4bd2751b2cd3ed0669f0a04c4998cc99229f Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Fri, 23 Jan 2026 18:28:05 -0800 Subject: [PATCH 4/4] feat(desktop): improve StartView onboarding CTA design - Simplify layout with cleaner hierarchy - Use proper shadcn typography for headings - Remove Clone from GitHub option - Larger dropzone with dashed border and bg-card - Add folder icon with gradient background treatment - Update CTA text to "Drag and drop a git folder to open" - Remove loading animation in favor of simpler UX - Add subtle transition on drag state change --- .../main/components/StartView/index.tsx | 139 +++++++----------- 1 file changed, 53 insertions(+), 86 deletions(-) diff --git a/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx b/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx index a3b68d4930f..d6b1f25bcf1 100644 --- a/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx +++ b/apps/desktop/src/renderer/screens/main/components/StartView/index.tsx @@ -1,11 +1,9 @@ -import { Button } from "@superset/ui/button"; import { cn } from "@superset/ui/utils"; import { useNavigate } from "@tanstack/react-router"; import { useCallback, useEffect, useState } from "react"; -import { LuFolderOpen, LuLoader, LuX } from "react-icons/lu"; +import { LuFolderOpen, LuX } from "react-icons/lu"; import { useOpenFromPath, useOpenNew } from "renderer/react-query/projects"; import { SupersetLogo } from "renderer/routes/sign-in/components/SupersetLogo"; -import { CloneRepoDialog } from "./CloneRepoDialog"; import { InitGitDialog } from "./InitGitDialog"; import { StartTopBar } from "./StartTopBar"; @@ -14,7 +12,6 @@ export function StartView() { const openNew = useOpenNew(); const openFromPath = useOpenFromPath(); const [error, setError] = useState(null); - const [isCloneDialogOpen, setIsCloneDialogOpen] = useState(false); const [initGitDialog, setInitGitDialog] = useState<{ isOpen: boolean; selectedPath: string; @@ -181,7 +178,7 @@ export function StartView() { onDragLeave={handleDragLeave} onDrop={handleDrop} > -
+
Welcome to @@ -189,101 +186,71 @@ export function StartView() {
-
-

- Open a project -

+
+

+ Open a Project +

- - - - {error && ( -
- {error} - + {isDragOver + ? "Drop to open" + : "Drag and drop a git folder to open"} +

+

+ Any folder with a .git directory +

- )} +
+ + {error && ( +
+ {error} + +
+ )}
- {/* Dialogs */} - setIsCloneDialogOpen(false)} - onError={setError} - />