Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/features/window/components/custom-title-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getCurrentWindow } from "@tauri-apps/api/window";
import { Maximize2, MenuIcon, Minimize2, Minus, SquareArrowOutUpRight, X } from "lucide-react";
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { openFolder } from "@/features/file-system/controllers/platform";
import { useFileSystemStore } from "@/features/file-system/controllers/store";
Expand Down Expand Up @@ -48,6 +48,7 @@ const CustomTitleBar = ({ showMinimal = false }: CustomTitleBarProps) => {
} = useUIState();

const [menuBarActiveMenu, setMenuBarActiveMenu] = useState<string | null>(null);
const menuButtonRef = useRef<HTMLButtonElement>(null);
const [isMaximized, setIsMaximized] = useState(false);
const [isFullscreen, setIsFullscreen] = useState(false);
const [currentWindow, setCurrentWindow] = useState<any>(null);
Expand Down Expand Up @@ -291,8 +292,9 @@ const CustomTitleBar = ({ showMinimal = false }: CustomTitleBarProps) => {
<div className="relative">
<Tooltip content="Menu" side="bottom">
<Button
ref={menuButtonRef}
onClick={() => {
setMenuBarActiveMenu("File");
setMenuBarActiveMenu((prev) => (prev ? null : "File"));
}}
variant="secondary"
size="icon-sm"
Expand All @@ -305,6 +307,7 @@ const CustomTitleBar = ({ showMinimal = false }: CustomTitleBarProps) => {
activeMenu={menuBarActiveMenu}
setActiveMenu={setMenuBarActiveMenu}
compactFloating
anchorRef={menuButtonRef}
/>
</div>
)}
Expand Down Expand Up @@ -358,8 +361,9 @@ const CustomTitleBar = ({ showMinimal = false }: CustomTitleBarProps) => {
<div className="relative mr-2">
<Tooltip content="Menu" side="bottom">
<Button
ref={menuButtonRef}
onClick={() => {
setMenuBarActiveMenu("File");
setMenuBarActiveMenu((prev) => (prev ? null : "File"));
}}
variant="secondary"
size="icon-sm"
Expand All @@ -372,6 +376,7 @@ const CustomTitleBar = ({ showMinimal = false }: CustomTitleBarProps) => {
activeMenu={menuBarActiveMenu}
setActiveMenu={setMenuBarActiveMenu}
compactFloating
anchorRef={menuButtonRef}
/>
</div>
)}
Expand Down
76 changes: 51 additions & 25 deletions src/features/window/components/menu-bar/window-menu-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ interface Props {
activeMenu: string | null;
setActiveMenu: React.Dispatch<React.SetStateAction<string | null>>;
compactFloating?: boolean;
anchorRef?: React.RefObject<HTMLButtonElement | null>;
}

const CustomMenuBar = ({ activeMenu, setActiveMenu, compactFloating = false }: Props) => {
const CustomMenuBar = ({
activeMenu,
setActiveMenu,
compactFloating = false,
anchorRef,
}: Props) => {
const { settings } = useSettingsStore();
const [themes, setThemes] = useState<ThemeDefinition[]>([]);
const menuBarRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -198,46 +204,66 @@ const CustomMenuBar = ({ activeMenu, setActiveMenu, compactFloating = false }: P
if (!activeMenu) return;

const handleMouseDown = (e: MouseEvent) => {
if (menuBarRef.current && !menuBarRef.current.contains(e.target as Node)) {
const target = e.target as Node;
const isInsideMenuBar = menuBarRef.current?.contains(target);
const isAnchorButton = anchorRef?.current?.contains(target);
if (!isInsideMenuBar && !isAnchorButton) {
setActiveMenu(null);
}
};

document.addEventListener("mousedown", handleMouseDown);
return () => document.removeEventListener("mousedown", handleMouseDown);
}, [activeMenu, setActiveMenu]);
}, [activeMenu, setActiveMenu, anchorRef]);

// In compact mode, hide entirely when no menu is open
if (settings.compactMenuBar && !activeMenu) return null;

return (
<div
ref={menuBarRef}
className={cn(
"z-[10030] flex h-6 items-center gap-0.5 rounded-full border border-border/70 bg-primary-bg/65 px-0.5 py-0.5",
settings.compactMenuBar &&
compactFloating &&
"absolute top-[calc(100%+4px)] left-0 rounded-2xl border border-border bg-primary-bg/95 px-1 py-1 shadow-xl backdrop-blur-sm",
settings.compactMenuBar &&
!compactFloating &&
"absolute inset-0 h-full rounded-none border-none bg-transparent px-2 py-0",
"z-[10030] flex flex-col",
settings.compactMenuBar && compactFloating && "absolute top-full left-0 mt-1",
settings.compactMenuBar && !compactFloating && "absolute inset-0",
)}
>
{Object.keys(menus).map((menuName) => (
<Button
key={menuName}
variant="ghost"
size="sm"
className={cn(
"ui-text-sm h-5 rounded-md px-1.5 text-text-lighter",
activeMenu === menuName ? "bg-hover/80 text-text" : "hover:bg-hover/50 hover:text-text",
)}
onClick={() => setActiveMenu((current) => (current === menuName ? null : menuName))}
>
{menuName}
</Button>
))}
{/* Horizontal tab bar */}
<div
className={cn(
"flex h-6 items-center gap-0.5 rounded-full border border-border/70 bg-primary-bg/65 px-0.5 py-0.5",
settings.compactMenuBar &&
compactFloating &&
"rounded-2xl border border-border bg-primary-bg/95 px-1 py-1 shadow-xl backdrop-blur-sm",
settings.compactMenuBar &&
!compactFloating &&
"h-full rounded-none border-none bg-transparent px-2 py-0",
)}
>
{Object.keys(menus).map((menuName) => (
<Button
key={menuName}
variant="ghost"
size="sm"
className={cn(
"ui-text-sm h-5 rounded-md px-1.5 text-text-lighter",
activeMenu === menuName
? "bg-hover/80 text-text"
: "hover:bg-hover/50 hover:text-text",
)}
onClick={() => setActiveMenu((current) => (current === menuName ? null : menuName))}
>
{menuName}
</Button>
))}
</div>

{activeMenu && menus[activeMenu as keyof typeof menus]}
{/* Dropdown — rendered below the tab bar, not overlapping it */}
{activeMenu && (
<div className="z-[10031] mt-1 w-max min-w-[180px]">
{menus[activeMenu as keyof typeof menus]}
</div>
)}
</div>
);
};
Expand Down