From 9b351e612cd456a782e9a6f57a5c9f5f419a7b34 Mon Sep 17 00:00:00 2001
From: ZhenYi <434836402@qq.com>
Date: Fri, 15 May 2026 13:11:07 +0800
Subject: [PATCH] feat(ui): improve header layout and add theme preset selector
- Update Header with better user menu and navigation layout
- Add ThemePresetSelector component for theme customization
- Refine ChannelSidebar with improved visual hierarchy
---
src/components/layout/ChannelSidebar.tsx | 7 ++-
src/components/layout/Header.tsx | 22 ++++++---
src/components/theme/ThemePresetSelector.tsx | 51 ++++++++++++++++++++
3 files changed, 71 insertions(+), 9 deletions(-)
diff --git a/src/components/layout/ChannelSidebar.tsx b/src/components/layout/ChannelSidebar.tsx
index 9a586dd..f6bbdcb 100644
--- a/src/components/layout/ChannelSidebar.tsx
+++ b/src/components/layout/ChannelSidebar.tsx
@@ -5,6 +5,8 @@ import {useProjectInfo} from "@/hooks/useProjectInfo";
import {Hash, PanelLeftClose, Plus, Search, Settings} from "lucide-react";
import {CHANNEL_SIDEBAR} from "@/css/layout/styles";
import {ProjectCreateMenuModal} from "@/app/project";
+import { isProjectAdminRole } from "@/lib/project-permissions";
+import { openGlobalSearch } from "@/components/search/global-search-events";
const NAV_ITEMS = [
{
@@ -60,7 +62,7 @@ export const ChannelSidebar = memo(function ChannelSidebar({onCollapse}: Channel
const isSettingsActive = isActive("settings");
- const showSettings = projectInfo?.role === "Owner" || projectInfo?.role === "Admin";
+ const showSettings = isProjectAdminRole(projectInfo?.role);
const uncategorizedRooms = useMemo(
() => rooms.filter((r) => !r.isMuted && !r.category),
@@ -98,9 +100,10 @@ export const ChannelSidebar = memo(function ChannelSidebar({onCollapse}: Channel
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index 90a915f..1829707 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -5,6 +5,8 @@ import { useProjectLayout } from "@/app/project/layout";
import { useProjectsQuery } from "@/hooks/useProjectsQuery";
import { useOptionalRoom } from "@/contexts/room";
import { RoomSettingsModal } from "@/app/project/channel/RoomSettingsModal";
+import { isProjectAdminRole } from "@/lib/project-permissions";
+import { openGlobalSearch } from "@/components/search/global-search-events";
import {
DropdownMenu,
DropdownMenuTrigger,
@@ -42,15 +44,18 @@ const ME_NAV_SIBLINGS: BreadcrumbSibling[] = [
{ label: "Invitations", path: "/me/invitations" },
];
-function getProjectNavSiblings(projectName: string): BreadcrumbSibling[] {
- return [
+function getProjectNavSiblings(projectName: string, showSettings: boolean): BreadcrumbSibling[] {
+ const items = [
{ label: "Repository", path: `/${projectName}/repos` },
{ label: "Issues", path: `/${projectName}/issues` },
{ label: "Skills", path: `/${projectName}/skills` },
{ label: "Board", path: `/${projectName}/board` },
{ label: "Chat", path: `/${projectName}/chat` },
- { label: "Settings", path: `/${projectName}/settings` },
];
+ if (showSettings) {
+ items.push({ label: "Settings", path: `/${projectName}/settings` });
+ }
+ return items;
}
function getRepoTabSiblings(repoBasePath: string): BreadcrumbSibling[] {
@@ -67,6 +72,7 @@ function getRepoTabSiblings(repoBasePath: string): BreadcrumbSibling[] {
function getSegmentSiblings(
segment: BreadcrumbSegment,
projects: Array<{ name: string; display_name: string }>,
+ canManageProject = false,
): BreadcrumbSibling[] | null {
if (segment.isLast) return null;
@@ -86,7 +92,7 @@ function getSegmentSiblings(
if (depth === 2) {
if (parts[0] === "me") return ME_NAV_SIBLINGS;
- return getProjectNavSiblings(parts[0]);
+ return getProjectNavSiblings(parts[0], canManageProject);
}
if (depth >= 3 && parts[1] === "repo") {
@@ -168,9 +174,10 @@ const TOOLBAR_ICONS = [
export const Header = memo(function Header() {
const location = useLocation();
const { segments, projects } = useBreadcrumbs();
- const { isProjectMember, showMembers, setShowMembers } = useProjectLayout();
+ const { isProjectMember, projectInfo, showMembers, setShowMembers } = useProjectLayout();
const [showSettings, setShowSettings] = useState(false);
const roomContext = useOptionalRoom();
+ const canManageProject = isProjectAdminRole(projectInfo?.role);
const handleCopy = useCallback((e: React.MouseEvent, text: string) => {
e.preventDefault();
@@ -195,7 +202,7 @@ export const Header = memo(function Header() {
{segments.map((segment, idx) => {
- const siblings = getSegmentSiblings(segment, projects);
+ const siblings = getSegmentSiblings(segment, projects, canManageProject);
return (
@@ -297,9 +304,10 @@ export const Header = memo(function Header() {
{TOOLBAR_ICONS.map((icon, i) => (