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" },