Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
}

.mosaic-theme-dark .mosaic-window {
border: 1px solid var(--color-border);
border: none;
border-radius: var(--radius-md);
overflow: hidden;
box-shadow: none;
transition:
border-color 0.15s ease,
box-shadow 0.15s ease;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export function ContentView() {

if (currentMode === SidebarMode.Changes) {
return (
<div className="h-full p-1 bg-tertiary">
<div className="h-full bg-background rounded-lg">
<div className="h-full overflow-hidden bg-tertiary p-1">
<div className="h-full bg-background rounded-lg overflow-hidden border border-border">
<ChangesContent />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,15 @@ export function ChangesView() {

if (!worktreePath) {
return (
<div className="flex-1 flex items-center justify-center text-muted-foreground text-sm">
<div className="flex-1 flex items-center justify-center text-muted-foreground text-sm p-2">
No workspace selected
</div>
);
}

if (isLoading) {
return (
<div className="flex-1 flex items-center justify-center text-muted-foreground text-sm">
<div className="flex-1 flex items-center justify-center text-muted-foreground text-sm p-2">
Loading changes...
</div>
);
Expand All @@ -172,7 +172,7 @@ export function ChangesView() {
!status.untracked
) {
return (
<div className="flex-1 flex items-center justify-center text-muted-foreground text-sm">
<div className="flex-1 flex items-center justify-center text-muted-foreground text-sm p-2">
Unable to load changes
</div>
);
Expand All @@ -197,7 +197,7 @@ export function ChangesView() {
const prUrl = githubStatus?.pr?.url;

return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full p-2">
<ChangesHeader
ahead={status.ahead}
behind={status.behind}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function Sidebar() {
const modes: SidebarMode[] = [SidebarMode.Tabs, SidebarMode.Changes];

return (
<aside className="h-full flex flex-col overflow-hidden border-r border-border/50">
<aside className="h-full flex flex-col overflow-hidden">
<ModeCarousel
modes={modes}
currentMode={currentMode}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { WorkspaceFooterLeft } from "./components/WorkspaceFooterLeft";
import { WorkspaceFooterRight } from "./components/WorkspaceFooterRight";

interface WorkspaceFooterProps {
worktreePath: string | undefined;
}

export function WorkspaceFooter({ worktreePath }: WorkspaceFooterProps) {
if (!worktreePath) return null;

return (
<div className="pl-1 h-8 w-full flex items-center text-xs shrink-0 select-none bg-tertiary">
<div className="flex items-center gap-2 min-w-0">
<WorkspaceFooterLeft />
</div>
<div className="ml-auto flex items-center h-full mr-2">
<WorkspaceFooterRight worktreePath={worktreePath} />
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
import { GoGitBranch } from "react-icons/go";
import { trpc } from "renderer/lib/trpc";
import { HelpMenu } from "../../../../HelpMenu";

export function WorkspaceFooterLeft() {
const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
const currentBranch = activeWorkspace?.worktree?.branch;
const baseBranch = activeWorkspace?.worktree?.baseBranch;
return (
<>
<HelpMenu />
{currentBranch && (
<Tooltip>
<TooltipTrigger asChild>
<span className="flex items-center gap-1.5 min-w-0">
<GoGitBranch className="size-3.5 text-foreground/60 shrink-0" />
<span className="max-w-[180px] truncate text-foreground/90 font-medium">
{currentBranch}
</span>
</span>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={8}>
Current branch
</TooltipContent>
</Tooltip>
)}
{baseBranch && baseBranch !== currentBranch && (
<Tooltip>
<TooltipTrigger asChild>
<span className="flex items-center gap-1.5 text-foreground/50">
<span className="shrink-0">from</span>
<span className="text-foreground/70 truncate">{baseBranch}</span>
</span>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={8}>
Based on {baseBranch}
</TooltipContent>
</Tooltip>
)}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { WorkspaceFooterLeft } from "./WorkspaceFooterLeft";
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@superset/ui/dropdown-menu";
import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
import type { ExternalApp } from "main/lib/db/schemas";
import { HiChevronDown } from "react-icons/hi2";
import { LuArrowUpRight, LuCopy } from "react-icons/lu";
import jetbrainsIcon from "renderer/assets/app-icons/jetbrains.svg";
import {
APP_OPTIONS,
getAppOption,
JETBRAINS_OPTIONS,
} from "renderer/components/OpenInButton";
import { shortenHomePath } from "renderer/lib/formatPath";
import { trpc } from "renderer/lib/trpc";

interface FormattedPath {
prefix: string;
worktreeName: string;
}

function formatWorktreePath(
path: string,
homeDir: string | undefined,
): FormattedPath {
const shortenedPath = shortenHomePath(path, homeDir);

// Split into prefix and worktree name (last segment)
const lastSlashIndex = shortenedPath.lastIndexOf("/");
if (lastSlashIndex !== -1) {
return {
prefix: shortenedPath.slice(0, lastSlashIndex + 1),
worktreeName: shortenedPath.slice(lastSlashIndex + 1),
};
}

return { prefix: "", worktreeName: shortenedPath };
}

interface WorkspaceFooterRightProps {
worktreePath: string;
}

export function WorkspaceFooterRight({
worktreePath,
}: WorkspaceFooterRightProps) {
const { data: homeDir } = trpc.window.getHomeDir.useQuery();
const utils = trpc.useUtils();
const { data: lastUsedApp = "cursor" } =
trpc.settings.getLastUsedApp.useQuery();
const openInApp = trpc.external.openInApp.useMutation({
onSuccess: () => utils.settings.getLastUsedApp.invalidate(),
});
const copyPath = trpc.external.copyPath.useMutation();

const formattedPath = formatWorktreePath(worktreePath, homeDir);
const currentApp = getAppOption(lastUsedApp);

const handleOpenInEditor = () => {
openInApp.mutate({ path: worktreePath, app: lastUsedApp });
};

const handleOpenInOtherApp = (appId: ExternalApp) => {
openInApp.mutate({ path: worktreePath, app: appId });
};

const handleCopyPath = () => {
copyPath.mutate(worktreePath);
};

const BUTTON_HEIGHT = 24;

return (
<>
{/* Path - clickable to open */}
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleOpenInEditor}
style={{ height: `${BUTTON_HEIGHT}px` }}
className="group flex items-center gap-1.5 pl-2 pr-1 rounded-l border border-r-0 border-foreground/20 bg-foreground/5 hover:bg-foreground/10 text-[11px] leading-none font-mono font-medium truncate max-w-[480px] transition-colors"
>
<img
src={currentApp.icon}
alt={currentApp.label}
className="size-3.5 object-contain shrink-0"
/>
<span className="text-foreground/60 group-hover:text-foreground/80 transition-colors">
{formattedPath.prefix}
</span>
<span className="text-foreground font-semibold">
{formattedPath.worktreeName}
</span>
<LuArrowUpRight className="size-3 -translate-y-px opacity-0 group-hover:opacity-70 transition-opacity text-foreground/80 shrink-0" />
</button>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={8}>
<span className="flex items-center gap-1.5">
Open in {currentApp.label}
<kbd className="px-1.5 py-0.5 text-[10px] font-sans bg-foreground/10 rounded">
⌘O
</kbd>
</span>
</TooltipContent>
</Tooltip>

{/* Open dropdown button */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type="button"
style={{ height: `${BUTTON_HEIGHT}px` }}
className="flex items-center gap-1 px-2 rounded-r border border-foreground/20 bg-foreground/5 hover:bg-foreground/10 text-foreground/90 transition-colors"
>
<span className="text-[11px] text-foreground font-semibold">
Open
</span>
<HiChevronDown className="size-3 text-foreground/60" />
</button>
</DropdownMenuTrigger>

<DropdownMenuContent align="end" className="w-52">
{APP_OPTIONS.map((app) => (
<DropdownMenuItem
key={app.id}
onClick={() => handleOpenInOtherApp(app.id)}
>
<img
src={app.icon}
alt={app.label}
className="size-4 object-contain mr-2"
/>
{app.label}
{app.id === lastUsedApp && (
<DropdownMenuShortcut>⌘O</DropdownMenuShortcut>
)}
</DropdownMenuItem>
))}
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<img
src={jetbrainsIcon}
alt="JetBrains"
className="size-4 object-contain mr-2"
/>
JetBrains
</DropdownMenuSubTrigger>
<DropdownMenuSubContent className="w-44">
{JETBRAINS_OPTIONS.map((app) => (
<DropdownMenuItem
key={app.id}
onClick={() => handleOpenInOtherApp(app.id)}
>
<img
src={app.icon}
alt={app.label}
className="size-4 object-contain mr-2"
/>
{app.label}
</DropdownMenuItem>
))}
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleCopyPath}>
<LuCopy className="size-4 mr-2" />
Copy path
<DropdownMenuShortcut>⌘⇧C</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { WorkspaceFooterRight } from "./WorkspaceFooterRight";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { WorkspaceFooter } from "./WorkspaceFooter";
Loading