diff --git a/apps/desktop/src/lib/trpc/routers/projects/projects.ts b/apps/desktop/src/lib/trpc/routers/projects/projects.ts index 09f9311fb6e..5bb47f62451 100644 --- a/apps/desktop/src/lib/trpc/routers/projects/projects.ts +++ b/apps/desktop/src/lib/trpc/routers/projects/projects.ts @@ -22,7 +22,7 @@ import { getGitRoot, refreshDefaultBranch, } from "../workspaces/utils/git"; -import { assignRandomColor } from "./utils/colors"; +import { getDefaultProjectColor } from "./utils/colors"; import { fetchGitHubOwner, getGitHubAvatarUrl } from "./utils/github"; type Project = SelectProject; @@ -70,7 +70,7 @@ function upsertProject(mainRepoPath: string, defaultBranch: string): Project { .values({ mainRepoPath, name, - color: assignRandomColor(), + color: getDefaultProjectColor(), defaultBranch, }) .returning() @@ -481,7 +481,7 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => { .values({ mainRepoPath: clonePath, name, - color: assignRandomColor(), + color: getDefaultProjectColor(), defaultBranch, }) .returning() diff --git a/apps/desktop/src/lib/trpc/routers/projects/utils/colors/colors.ts b/apps/desktop/src/lib/trpc/routers/projects/utils/colors/colors.ts index 656864ad077..f28bad30d64 100644 --- a/apps/desktop/src/lib/trpc/routers/projects/utils/colors/colors.ts +++ b/apps/desktop/src/lib/trpc/routers/projects/utils/colors/colors.ts @@ -1,7 +1,9 @@ -import { PROJECT_COLOR_VALUES } from "shared/constants/project-colors"; +import { PROJECT_COLOR_DEFAULT } from "shared/constants/project-colors"; -export function assignRandomColor(): string { - return PROJECT_COLOR_VALUES[ - Math.floor(Math.random() * PROJECT_COLOR_VALUES.length) - ]; +/** + * Returns the default color for new projects. + * Projects start with no custom color (gray border). + */ +export function getDefaultProjectColor(): string { + return PROJECT_COLOR_DEFAULT; } diff --git a/apps/desktop/src/lib/trpc/routers/projects/utils/colors/index.ts b/apps/desktop/src/lib/trpc/routers/projects/utils/colors/index.ts index 71f1ed4900e..596c5ecceef 100644 --- a/apps/desktop/src/lib/trpc/routers/projects/utils/colors/index.ts +++ b/apps/desktop/src/lib/trpc/routers/projects/utils/colors/index.ts @@ -1 +1 @@ -export { assignRandomColor } from "./colors"; +export { getDefaultProjectColor } from "./colors"; 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 26a5c949517..33683b92e65 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 @@ -3,6 +3,9 @@ import { ContextMenuContent, ContextMenuItem, ContextMenuSeparator, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, ContextMenuTrigger, } from "@superset/ui/context-menu"; import { @@ -15,15 +18,21 @@ import { toast } from "@superset/ui/sonner"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { cn } from "@superset/ui/utils"; import { HiChevronRight, HiMiniPlus, HiOutlineBolt } from "react-icons/hi2"; -import { LuFolderOpen, LuSettings, LuX } from "react-icons/lu"; +import { LuFolderOpen, LuPalette, LuSettings, LuX } from "react-icons/lu"; import { trpc } from "renderer/lib/trpc"; +import { useUpdateProject } from "renderer/react-query/projects/useUpdateProject"; import { useOpenSettings } from "renderer/stores/app-state"; +import { + PROJECT_COLOR_DEFAULT, + PROJECT_COLORS, +} from "shared/constants/project-colors"; import { STROKE_WIDTH } from "../constants"; import { ProjectThumbnail } from "./ProjectThumbnail"; interface ProjectHeaderProps { projectId: string; projectName: string; + projectColor: string; githubOwner: string | null; mainRepoPath: string; /** Whether the project section is collapsed (workspaces hidden) */ @@ -42,6 +51,7 @@ interface ProjectHeaderProps { export function ProjectHeader({ projectId, projectName, + projectColor, githubOwner, mainRepoPath, isCollapsed, @@ -87,6 +97,48 @@ export function ProjectHeader({ openSettings("project"); }; + const updateProject = useUpdateProject({ + onError: (error) => toast.error(`Failed to update color: ${error.message}`), + }); + + const handleColorChange = (color: string) => { + updateProject.mutate({ id: projectId, patch: { color } }); + }; + + // Color picker submenu used in both collapsed and expanded context menus + const colorPickerSubmenu = ( + + + + Set Color + + + {PROJECT_COLORS.map((color) => { + const isDefault = color.value === PROJECT_COLOR_DEFAULT; + return ( + handleColorChange(color.value)} + className="flex items-center gap-2" + > + + {color.name} + {projectColor === color.value && ( + + )} + + ); + })} + + + ); + // Collapsed sidebar: show just the thumbnail with tooltip and context menu if (isSidebarCollapsed) { return ( @@ -105,6 +157,7 @@ export function ProjectHeader({ @@ -126,6 +179,7 @@ export function ProjectHeader({ Project Settings + {colorPickerSubmenu} {projectName} @@ -244,6 +299,7 @@ export function ProjectHeader({ Project Settings + {colorPickerSubmenu} {firstLetter} 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 6c0576282d8..e3e65281c65 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -37,6 +37,7 @@ export function WorkspaceSidebar({ key={group.project.id} projectId={group.project.id} projectName={group.project.name} + projectColor={group.project.color} githubOwner={group.project.githubOwner} mainRepoPath={group.project.mainRepoPath} workspaces={group.workspaces} diff --git a/apps/desktop/src/shared/constants/project-colors.ts b/apps/desktop/src/shared/constants/project-colors.ts index dc3776be19b..6b15426b89d 100644 --- a/apps/desktop/src/shared/constants/project-colors.ts +++ b/apps/desktop/src/shared/constants/project-colors.ts @@ -1,4 +1,8 @@ +/** Special value representing "no custom color" - uses default gray border */ +export const PROJECT_COLOR_DEFAULT = "default"; + export const PROJECT_COLORS: { name: string; value: string }[] = [ + { name: "Default", value: PROJECT_COLOR_DEFAULT }, { name: "Blue", value: "#3b82f6" }, { name: "Green", value: "#22c55e" }, { name: "Yellow", value: "#eab308" },