Merge branch 'dev' into cursor/OPEN-2835-fix-xmlparserblock-list-object-error-29c3

This commit is contained in:
Nicholas Tindle
2025-12-30 08:12:25 -06:00
committed by GitHub
7 changed files with 182 additions and 209 deletions

View File

@@ -13011,7 +13011,7 @@ snapshots:
minimatch: 3.1.2
node-abort-controller: 3.1.1
schema-utils: 3.3.0
semver: 7.7.2
semver: 7.7.3
tapable: 2.2.3
typescript: 5.9.3
webpack: 5.101.3(esbuild@0.25.9)

View File

@@ -1,13 +1,157 @@
"use client";
import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store";
import { okData } from "@/app/api/helpers";
import { IconAutoGPTLogo, IconType } from "@/components/__legacy__/ui/icons";
import { PreviewBanner } from "@/components/layout/Navbar/components/PreviewBanner/PreviewBanner";
import { useBreakpoint } from "@/lib/hooks/useBreakpoint";
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { environment } from "@/services/environment";
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
import { AccountMenu } from "./components/AccountMenu/AccountMenu";
import { AgentActivityDropdown } from "./components/AgentActivityDropdown/AgentActivityDropdown";
import { LoginButton } from "./components/LoginButton";
import { MobileNavBar } from "./components/MobileNavbar/MobileNavBar";
import { NavbarLink } from "./components/NavbarLink";
import { NavbarLoading } from "./components/NavbarLoading";
import { Wallet } from "./components/Wallet/Wallet";
import { getAccountMenuItems, loggedInLinks, loggedOutLinks } from "./helpers";
import { NavbarView } from "./components/NavbarView";
import { getNavbarAccountData } from "./data";
export async function Navbar() {
const { isLoggedIn } = await getNavbarAccountData();
export function Navbar() {
const { user, isLoggedIn, isUserLoading } = useSupabase();
const breakpoint = useBreakpoint();
const isSmallScreen = breakpoint === "sm" || breakpoint === "base";
const dynamicMenuItems = getAccountMenuItems(user?.role);
const isChatEnabled = useGetFlag(Flag.CHAT);
const previewBranchName = environment.getPreviewStealingDev();
const { data: profile, isLoading: isProfileLoading } = useGetV2GetUserProfile(
{
query: {
select: okData,
enabled: isLoggedIn && !!user,
// Include user ID in query key to ensure cache invalidation when user changes
queryKey: ["/api/store/profile", user?.id],
},
},
);
const isLoadingProfile = isProfileLoading || isUserLoading;
const shouldShowPreviewBanner = Boolean(isLoggedIn && previewBranchName);
const actualLoggedInLinks =
isChatEnabled === true
? loggedInLinks.concat([{ name: "Chat", href: "/chat" }])
: loggedInLinks;
if (isUserLoading) {
return <NavbarLoading />;
}
return (
<NavbarView isLoggedIn={isLoggedIn} previewBranchName={previewBranchName} />
<>
<div className="sticky top-0 z-40 w-full">
{shouldShowPreviewBanner && previewBranchName ? (
<PreviewBanner branchName={previewBranchName} />
) : null}
<nav className="border-zinc-[#EFEFF0] inline-flex h-[60px] w-full items-center border border-[#EFEFF0] bg-[#F3F4F6]/20 p-3 backdrop-blur-[26px]">
{/* Left section */}
{!isSmallScreen ? (
<div className="flex flex-1 items-center gap-5">
{isLoggedIn
? actualLoggedInLinks.map((link) => (
<NavbarLink
key={link.name}
name={link.name}
href={link.href}
/>
))
: loggedOutLinks.map((link) => (
<NavbarLink
key={link.name}
name={link.name}
href={link.href}
/>
))}
</div>
) : null}
{/* Centered logo */}
<div className="static h-auto w-[4.5rem] md:absolute md:left-1/2 md:top-1/2 md:w-[5.5rem] md:-translate-x-1/2 md:-translate-y-1/2">
<IconAutoGPTLogo className="h-full w-full" />
</div>
{/* Right section */}
{isLoggedIn && !isSmallScreen ? (
<div className="flex flex-1 items-center justify-end gap-4">
<div className="flex items-center gap-4">
<AgentActivityDropdown />
{profile && <Wallet key={profile.username} />}
<AccountMenu
userName={profile?.username}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url ?? ""}
menuItemGroups={dynamicMenuItems}
isLoading={isLoadingProfile}
/>
</div>
</div>
) : !isLoggedIn ? (
<div className="flex w-full items-center justify-end">
<LoginButton />
</div>
) : null}
{/* <ThemeToggle /> */}
</nav>
</div>
{/* Mobile Navbar - Adjust positioning */}
<>
{isLoggedIn && isSmallScreen ? (
<div className="fixed right-0 top-2 z-50 flex items-center gap-0">
<Wallet />
<MobileNavBar
userName={profile?.username}
menuItemGroups={[
{
groupName: "Navigation",
items: actualLoggedInLinks
.map((link) => {
if (link.name === "Chat" && !isChatEnabled) {
return null;
}
return {
icon:
link.name === "Marketplace"
? IconType.Marketplace
: link.name === "Library"
? IconType.Library
: link.name === "Build"
? IconType.Builder
: link.name === "Chat"
? IconType.Chat
: link.name === "Monitor"
? IconType.Library
: IconType.LayoutDashboard,
text: link.name,
href: link.href,
};
})
.filter((item) => item !== null) as Array<{
icon: IconType;
text: string;
href: string;
}>,
},
...dynamicMenuItems,
]}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url ?? ""}
/>
</div>
) : null}
</>
</>
);
}

View File

@@ -6,45 +6,42 @@ import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { cn } from "@/lib/utils";
import * as Sentry from "@sentry/nextjs";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useTransition } from "react";
export function AccountLogoutOption() {
const [isLoggingOut, setIsLoggingOut] = useState(false);
const [isPending, startTransition] = useTransition();
const supabase = useSupabase();
const router = useRouter();
const { toast } = useToast();
async function handleLogout() {
setIsLoggingOut(true);
try {
await supabase.logOut();
router.push("/login");
} catch (e) {
Sentry.captureException(e);
toast({
title: "Error logging out",
description:
"Something went wrong when logging out. Please try again. If the problem persists, please contact support.",
variant: "destructive",
});
} finally {
setTimeout(() => {
setIsLoggingOut(false);
}, 3000);
}
function handleLogout() {
startTransition(async () => {
try {
await supabase.logOut();
router.replace("/login");
} catch (e) {
Sentry.captureException(e);
toast({
title: "Error logging out",
description:
"Something went wrong when logging out. Please try again. If the problem persists, please contact support.",
variant: "destructive",
});
}
});
}
return (
<div
className={cn(
"inline-flex w-full items-center justify-start gap-2.5",
isLoggingOut && "justify-center opacity-50",
isPending && "justify-center opacity-50",
)}
onClick={handleLogout}
role="button"
tabIndex={0}
>
{isLoggingOut ? (
{isPending ? (
<LoadingSpinner className="size-5" />
) : (
<>

View File

@@ -5,16 +5,15 @@ export function NavbarLoading() {
return (
<nav className="sticky top-0 z-40 hidden h-16 items-center rounded-bl-2xl rounded-br-2xl border border-white/50 bg-white/5 p-3 backdrop-blur-[26px] md:inline-flex">
<div className="flex flex-1 items-center gap-6">
<Skeleton className="h-4 w-20 bg-white/20" />
<Skeleton className="h-4 w-16 bg-white/20" />
<Skeleton className="h-4 w-12 bg-white/20" />
<Skeleton className="h-6 w-24 bg-zinc-200/60" />
<Skeleton className="h-6 w-24 bg-zinc-200/60" />
<Skeleton className="h-6 w-16 bg-zinc-200/60" />
</div>
<div className="absolute left-1/2 top-1/2 h-10 w-[88.87px] -translate-x-1/2 -translate-y-1/2">
<IconAutoGPTLogo className="h-full w-full" />
</div>
<div className="flex flex-1 items-center justify-end gap-4">
<Skeleton className="h-8 w-8 rounded-full bg-white/20" />
<Skeleton className="h-8 w-8 rounded-full bg-white/20" />
<Skeleton className="h-8 w-8 rounded-full bg-zinc-200/60" />
</div>
</nav>
);

View File

@@ -1,144 +0,0 @@
"use client";
import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store";
import { IconAutoGPTLogo, IconType } from "@/components/__legacy__/ui/icons";
import { PreviewBanner } from "@/components/layout/Navbar/components/PreviewBanner/PreviewBanner";
import { useBreakpoint } from "@/lib/hooks/useBreakpoint";
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
import { useMemo } from "react";
import { okData } from "@/app/api/helpers";
import { getAccountMenuItems, loggedInLinks, loggedOutLinks } from "../helpers";
import { AccountMenu } from "./AccountMenu/AccountMenu";
import { AgentActivityDropdown } from "./AgentActivityDropdown/AgentActivityDropdown";
import { LoginButton } from "./LoginButton";
import { MobileNavBar } from "./MobileNavbar/MobileNavBar";
import { NavbarLink } from "./NavbarLink";
import { Wallet } from "./Wallet/Wallet";
interface NavbarViewProps {
isLoggedIn: boolean;
previewBranchName?: string | null;
}
export function NavbarView({ isLoggedIn, previewBranchName }: NavbarViewProps) {
const { user } = useSupabase();
const breakpoint = useBreakpoint();
const isSmallScreen = breakpoint === "sm" || breakpoint === "base";
const dynamicMenuItems = getAccountMenuItems(user?.role);
const isChatEnabled = useGetFlag(Flag.CHAT);
const { data: profile, isLoading: isProfileLoading } = useGetV2GetUserProfile(
{
query: {
select: okData,
enabled: isLoggedIn && !!user,
// Include user ID in query key to ensure cache invalidation when user changes
queryKey: ["/api/store/profile", user?.id],
},
},
);
const { isUserLoading } = useSupabase();
const isLoadingProfile = isProfileLoading || isUserLoading;
const linksWithChat = useMemo(() => {
const chatLink = { name: "Chat", href: "/chat" };
return isChatEnabled ? [...loggedInLinks, chatLink] : loggedInLinks;
}, [isChatEnabled]);
const shouldShowPreviewBanner = Boolean(isLoggedIn && previewBranchName);
return (
<>
<div className="sticky top-0 z-40 w-full">
{shouldShowPreviewBanner && previewBranchName ? (
<PreviewBanner branchName={previewBranchName} />
) : null}
<nav className="border-zinc-[#EFEFF0] inline-flex h-[60px] w-full items-center border border-[#EFEFF0] bg-[#F3F4F6]/20 p-3 backdrop-blur-[26px]">
{/* Left section */}
{!isSmallScreen ? (
<div className="flex flex-1 items-center gap-5">
{isLoggedIn
? linksWithChat.map((link) => (
<NavbarLink
key={link.name}
name={link.name}
href={link.href}
/>
))
: loggedOutLinks.map((link) => (
<NavbarLink
key={link.name}
name={link.name}
href={link.href}
/>
))}
</div>
) : null}
{/* Centered logo */}
<div className="static h-auto w-[4.5rem] md:absolute md:left-1/2 md:top-1/2 md:w-[5.5rem] md:-translate-x-1/2 md:-translate-y-1/2">
<IconAutoGPTLogo className="h-full w-full" />
</div>
{/* Right section */}
{isLoggedIn && !isSmallScreen ? (
<div className="flex flex-1 items-center justify-end gap-4">
<div className="flex items-center gap-4">
<AgentActivityDropdown />
{profile && <Wallet key={profile.username} />}
<AccountMenu
userName={profile?.username}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url ?? ""}
menuItemGroups={dynamicMenuItems}
isLoading={isLoadingProfile}
/>
</div>
</div>
) : !isLoggedIn ? (
<div className="flex w-full items-center justify-end">
<LoginButton />
</div>
) : null}
{/* <ThemeToggle /> */}
</nav>
</div>
{/* Mobile Navbar - Adjust positioning */}
<>
{isLoggedIn && isSmallScreen ? (
<div className="fixed right-0 top-2 z-50 flex items-center gap-0">
<Wallet />
<MobileNavBar
userName={profile?.username}
menuItemGroups={[
{
groupName: "Navigation",
items: linksWithChat.map((link) => ({
icon:
link.name === "Marketplace"
? IconType.Marketplace
: link.name === "Library"
? IconType.Library
: link.name === "Build"
? IconType.Builder
: link.name === "Chat"
? IconType.Chat
: link.name === "Monitor"
? IconType.Library
: IconType.LayoutDashboard,
text: link.name,
href: link.href,
})),
},
...dynamicMenuItems,
]}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url ?? ""}
/>
</div>
) : null}
</>
</>
);
}

View File

@@ -1,25 +0,0 @@
import { prefetchGetV2GetUserProfileQuery } from "@/app/api/__generated__/endpoints/store/store";
import { getQueryClient } from "@/lib/react-query/queryClient";
import { getServerUser } from "@/lib/supabase/server/getServerUser";
export async function getNavbarAccountData() {
const { user } = await getServerUser();
const isLoggedIn = Boolean(user);
const queryClient = getQueryClient();
if (!isLoggedIn) {
return {
profile: null,
isLoggedIn,
};
}
try {
await prefetchGetV2GetUserProfileQuery(queryClient);
} catch (error) {
console.error("Error fetching profile:", error);
}
return {
isLoggedIn,
};
}

View File

@@ -2,7 +2,6 @@
import { DEFAULT_SEARCH_TERMS } from "@/app/(platform)/marketplace/components/HeroSection/helpers";
import { useFlags } from "launchdarkly-react-client-sdk";
import { environment } from "../environment";
export enum Flag {
BETA_BLOCKS = "beta-blocks",
@@ -40,7 +39,7 @@ const mockFlags = {
[Flag.BETA_BLOCKS]: [],
[Flag.NEW_BLOCK_MENU]: false,
[Flag.NEW_AGENT_RUNS]: false,
[Flag.GRAPH_SEARCH]: true,
[Flag.GRAPH_SEARCH]: false,
[Flag.ENABLE_ENHANCED_OUTPUT_HANDLING]: false,
[Flag.NEW_FLOW_EDITOR]: false,
[Flag.BUILDER_VIEW_SWITCH]: false,
@@ -48,17 +47,20 @@ const mockFlags = {
[Flag.AGENT_FAVORITING]: false,
[Flag.MARKETPLACE_SEARCH_TERMS]: DEFAULT_SEARCH_TERMS,
[Flag.ENABLE_PLATFORM_PAYMENT]: false,
[Flag.CHAT]: true,
[Flag.CHAT]: false,
};
export function useGetFlag<T extends Flag>(flag: T): FlagValues[T] | null {
const currentFlags = useFlags<FlagValues>();
const flagValue = currentFlags[flag];
const isCloud = environment.isCloud();
if ((isPwMockEnabled && !isCloud) || flagValue === undefined) {
const envEnabled = process.env.NEXT_PUBLIC_LAUNCHDARKLY_ENABLED === "true";
const clientId = process.env.NEXT_PUBLIC_LAUNCHDARKLY_CLIENT_ID;
const isLaunchDarklyConfigured = envEnabled && clientId;
if (!isLaunchDarklyConfigured || isPwMockEnabled) {
return mockFlags[flag];
}
return flagValue;
return flagValue ?? mockFlags[flag];
}