Skip to content

[TOOL-4827] Move Nebula out of dashboard #7356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 17, 2025
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
11 changes: 0 additions & 11 deletions apps/dashboard/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ NEXT_PUBLIC_STRIPE_KEY=

NEXT_PUBLIC_STRIPE_PAYMENT_METHOD_CFG_ID=

# Needed for contract analytics / blockchain data information
CHAINSAW_API_KEY=

#
# Private (server)
#
Expand All @@ -65,17 +62,11 @@ MORALIS_API_KEY=
# - not required to build (unless testing wallet NFTs)>
SSR_ALCHEMY_KEY=

# beehiiv.com API key (used for newsletter signups)
# - not required to build (unless testing newsletter signups)>
BEEHIIV_API_KEY=

# Hubspot Access Token (used for contact us form)
# - not required to build (unless testing contact us form)>
HUBSPOT_ACCESS_TOKEN=

# Github API Token (used for /open-source)
GITHUB_API_TOKEN="ghp_..."

# Upload server url
NEXT_PUBLIC_DASHBOARD_UPLOAD_SERVER="https://storage.thirdweb-preview.com"

Expand All @@ -100,8 +91,6 @@ REDIS_URL=""

ANALYTICS_SERVICE_URL=""

# Required for Nebula Chat
NEXT_PUBLIC_NEBULA_URL=""

# required for billing parts of the dashboard (team -> settings -> billing / invoices)
STRIPE_SECRET_KEY=""
Expand Down
1 change: 0 additions & 1 deletion apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
"compare-versions": "^6.1.0",
"date-fns": "4.1.0",
"fast-xml-parser": "^5.2.5",
"fetch-event-stream": "0.1.5",
"flat": "^6.0.1",
"framer-motion": "12.17.0",
"fuse.js": "7.1.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/@/components/Responsive.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import { ClientOnly } from "@/components/blocks/client-only";
import { Suspense } from "react";
import { ClientOnly } from "../../components/ClientOnly/ClientOnly";
import { useIsMobile } from "../hooks/use-mobile";

export function ResponsiveLayout(props: {
Expand Down
42 changes: 42 additions & 0 deletions apps/dashboard/src/@/components/blocks/client-only.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";

import { cn } from "@/lib/utils";
import { type ReactNode, useEffect, useState } from "react";

interface ClientOnlyProps {
/**
* Use this to server render a skeleton or loading state
*/
ssr: ReactNode;
className?: string;
children: ReactNode;
}

export const ClientOnly: React.FC<ClientOnlyProps> = ({
children,
ssr,
className,
}) => {
const hasMounted = useIsClientMounted();

if (!hasMounted) {
return <> {ssr} </>;
}

return (
<div className={cn("fade-in-0 fill-mode-forwards ease-in", className)}>
{children}
</div>
);
};

function useIsClientMounted() {
const [hasMounted, setHasMounted] = useState(false);

// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
setHasMounted(true);
}, []);

return hasMounted;
}
2 changes: 1 addition & 1 deletion apps/dashboard/src/@/components/color-mode-toggle.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { ClientOnly } from "@/components/blocks/client-only";
import { Button } from "@/components/ui/button";
import { ClientOnly } from "components/ClientOnly/ClientOnly";
import { MoonIcon, SunIcon } from "lucide-react";
import { useTheme } from "next-themes";
import { Skeleton } from "./ui/skeleton";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
inset: 0px;
border-radius: 50%;
border: 4px solid #fff;
animation: prixClipFix 2s linear infinite;
stroke-linecap: round;
animation: dash 1.5s ease-in-out infinite;
}
Expand Down
13 changes: 6 additions & 7 deletions apps/dashboard/src/@/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ const buttonVariants = cva(
{
variants: {
variant: {
primary:
"bg-primary hover:bg-primary/90 text-semibold text-primary-foreground ",
primary: "bg-primary hover:bg-primary/90 text-primary-foreground ",
default: "bg-foreground text-background hover:bg-foreground/90",
destructive:
"bg-destructive hover:bg-destructive/90 text-semibold text-destructive-foreground ",
"bg-destructive hover:bg-destructive/90 text-destructive-foreground ",
outline:
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground text-semibold",
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary hover:bg-secondary/80 text-semibold text-secondary-foreground ",
ghost: "hover:bg-accent text-semibold hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline text-semibold",
"bg-secondary hover:bg-secondary/80 text-secondary-foreground ",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "underline-offset-4 hover:underline",
pink: "border border-nebula-pink-foreground !text-nebula-pink-foreground bg-[hsl(var(--nebula-pink-foreground)/5%)] hover:bg-nebula-pink-foreground/10 dark:!text-foreground dark:bg-nebula-pink-foreground/10 dark:hover:bg-nebula-pink-foreground/20",
upsell:
"bg-green-600 text-white hover:bg-green-700 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200",
Expand Down
3 changes: 1 addition & 2 deletions apps/dashboard/src/@/components/ui/skeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function SkeletonContainer<T>(props: {
loadedData?: T;
skeletonData: T;
className?: string;
render: (data: T, isSkeleton: boolean) => React.ReactNode;
render: (data: T) => React.ReactNode;
style?: React.CSSProperties;
}) {
const isLoading = props.loadedData === undefined;
Expand All @@ -40,7 +40,6 @@ function SkeletonContainer<T>(props: {
props.loadedData === undefined
? props.skeletonData
: props.loadedData,
!isLoading,
)}
</div>
</div>
Expand Down
5 changes: 0 additions & 5 deletions apps/dashboard/src/@/constants/public-envs.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
export const NEXT_PUBLIC_DASHBOARD_CLIENT_ID =
process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID || "";

export const NEXT_PUBLIC_NEBULA_APP_CLIENT_ID =
process.env.NEXT_PUBLIC_NEBULA_APP_CLIENT_ID || "";

export const NEXT_PUBLIC_THIRDWEB_VAULT_URL =
process.env.NEXT_PUBLIC_THIRDWEB_VAULT_URL || "";

Expand All @@ -30,7 +27,5 @@ export const NEXT_PUBLIC_TURNSTILE_SITE_KEY =
export const NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET =
process.env.NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET || "";

export const NEXT_PUBLIC_NEBULA_URL = process.env.NEXT_PUBLIC_NEBULA_URL || "";

export const NEXT_PUBLIC_DEMO_ENGINE_URL =
process.env.NEXT_PUBLIC_DEMO_ENGINE_URL || "";
10 changes: 0 additions & 10 deletions apps/dashboard/src/@/constants/server-envs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,6 @@ experimental_taintUniqueValue(
DASHBOARD_THIRDWEB_SECRET_KEY,
);

export const NEBULA_APP_SECRET_KEY = process.env.NEBULA_APP_SECRET_KEY || "";

if (NEBULA_APP_SECRET_KEY) {
experimental_taintUniqueValue(
"Do not pass NEBULA_APP_SECRET_KEY to the client",
process,
NEBULA_APP_SECRET_KEY,
);
}

export const API_SERVER_SECRET = process.env.API_SERVER_SECRET || "";

if (API_SERVER_SECRET) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
import {
getAuthToken,
getAuthTokenWalletAddress,
} from "@app/api/lib/getAuthToken";
import { getAuthToken } from "@app/api/lib/getAuthToken";
import { ChevronDownIcon, TicketCheckIcon } from "lucide-react";
import type { Metadata } from "next";
import Link from "next/link";
import { redirect } from "next/navigation";
import { mapV4ChainToV5Chain } from "../../../../../../contexts/map-chains";
import { NebulaChatButton } from "../../../../../nebula-app/(app)/components/FloatingChat/FloatingChat";
import { TeamHeader } from "../../../../team/components/TeamHeader/team-header";
import { StarButton } from "../../components/client/star-button";
import { getChain, getChainMetadata } from "../../utils";
Expand Down Expand Up @@ -61,10 +57,9 @@ export default async function ChainPageLayout(props: {
}) {
const params = await props.params;
const { children } = props;
const [chain, authToken, accountAddress] = await Promise.all([
const [chain, authToken] = await Promise.all([
getChain(params.chain_id),
getAuthToken(),
getAuthTokenWalletAddress(),
]);

if (params.chain_id !== chain.slug) {
Expand All @@ -74,49 +69,12 @@ export default async function ChainPageLayout(props: {
const chainMetadata = await getChainMetadata(chain.chainId);
const client = getClientThirdwebClient(undefined);

const chainPromptPrefix = `\
You are assisting users exploring the chain ${chain.name} (Chain ID: ${chain.chainId}). Provide concise insights into the types of applications and activities prevalent on this chain, such as DeFi protocols, NFT marketplaces, or gaming platforms. Highlight notable projects or trends without delving into technical details like consensus mechanisms or gas fees.
Users may seek comparisons between ${chain.name} and other chains. Provide objective, succinct comparisons focusing on performance, fees, and ecosystem support. Refrain from transaction-specific advice unless requested.
Provide users with an understanding of the unique use cases and functionalities that ${chain.name} supports. Discuss how developers leverage this chain for specific applications, such as scalable dApps, low-cost transactions, or specialized token standards, focusing on practical implementations.
Users may be interested in utilizing thirdweb tools on ${chain.name}. Offer clear guidance on how thirdweb's SDKs, smart contract templates, and deployment tools integrate with this chain. Emphasize the functionalities enabled by thirdweb without discussing transaction execution unless prompted.
Avoid transaction-related actions to be executed by the user unless inquired about.

The following is the user's message:
`;

const examplePrompts: string[] = [
"What are users doing on this chain?",
"What are the most active contracts?",
"Why would I use this chain over others?",
"Can I deploy thirdweb contracts to this chain?",
];

if (chain.chainId !== 1) {
examplePrompts.push("Can I bridge assets from Ethereum to this chain?");
}

return (
<div className="flex grow flex-col">
<div className="border-border border-b bg-card">
<TeamHeader />
</div>
<NebulaChatButton
isLoggedIn={!!authToken}
networks={chain.testnet ? "testnet" : "mainnet"}
isFloating={true}
pageType="chain"
label="Ask AI about this chain"
client={client}
nebulaParams={{
messagePrefix: chainPromptPrefix,
chainIds: [chain.chainId],
wallet: accountAddress ?? undefined,
}}
examplePrompts={examplePrompts.map((prompt) => ({
title: prompt,
message: prompt,
}))}
/>

<div className="flex h-14 border-border border-b pl-7">
<Breadcrumb className="my-auto">
<BreadcrumbList>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import { notFound } from "next/navigation";
import { getContractMetadata } from "thirdweb/extensions/common";
import { isAddress, isContractDeployed } from "thirdweb/utils";
import { shortenIfAddress } from "utils/usedapp-external";
import { NebulaChatButton } from "../../../../../nebula-app/(app)/components/FloatingChat/FloatingChat";
import { examplePrompts } from "../../../../../nebula-app/(app)/data/examplePrompts";
import { getAuthTokenWalletAddress } from "../../../../api/lib/getAuthToken";
import type { ProjectMeta } from "../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types";
import { TeamHeader } from "../../../../team/components/TeamHeader/team-header";
import { ConfigureCustomChain } from "./_layout/ConfigureCustomChain";
Expand All @@ -34,13 +31,12 @@ export async function SharedContractLayout(props: {
return notFound();
}

const [info, accountAddress, teamsAndProjects] = await Promise.all([
const [info, teamsAndProjects] = await Promise.all([
getContractPageParamsInfo({
contractAddress: props.contractAddress,
chainIdOrSlug: props.chainIdOrSlug,
teamId: props.projectMeta?.teamId,
}),
getAuthTokenWalletAddress(),
getTeamsAndProjectsIfLoggedIn(),
]);

Expand Down Expand Up @@ -109,17 +105,6 @@ export async function SharedContractLayout(props: {
projectMeta: props.projectMeta,
});

const contractAddress = serverContract.address;
const chainName = chainMetadata.name;
const chainId = chainMetadata.chainId;

const contractPromptPrefix = `A user is viewing the contract address ${contractAddress} on ${chainName} (Chain ID: ${chainId}). Provide a concise summary of this contract's functionalities, such as token minting, staking, or governance mechanisms. Focus on what the contract enables users to do, avoiding transaction execution details unless requested.
Users may be interested in how to interact with the contract. Outline common interaction patterns, such as claiming rewards, participating in governance, or transferring assets. Emphasize the contract's capabilities without guiding through transaction processes unless asked.
Provide insights into how the contract is being used. Share information on user engagement, transaction volumes, or integration with other dApps, focusing on the contract's role within the broader ecosystem.
Users may be considering integrating the contract into their applications. Discuss how this contract's functionalities can be leveraged within different types of dApps, highlighting potential use cases and benefits.

The following is the user's message:`;

return (
<ConditionalTeamHeaderLayout projectMeta={props.projectMeta}>
<ContractPageLayout
Expand All @@ -131,20 +116,6 @@ The following is the user's message:`;
teamsAndProjects={teamsAndProjects}
projectMeta={props.projectMeta}
>
<NebulaChatButton
isLoggedIn={!!accountAddress}
networks={info.chainMetadata.testnet ? "testnet" : "mainnet"}
isFloating={true}
pageType="contract"
label="Ask AI about this contract"
client={clientContract.client}
nebulaParams={{
messagePrefix: contractPromptPrefix,
chainIds: [chainId],
wallet: accountAddress ?? undefined,
}}
examplePrompts={examplePrompts}
/>
{props.children}
</ContractPageLayout>
</ConditionalTeamHeaderLayout>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { NebulaIcon } from "../../../../../nebula-app/(app)/icons/NebulaIcon";
import type { ChainSupportedService } from "../../types/chain";
import { ConnectSDKIcon } from "./icons/ConnectSDKIcon";
import { ContractIcon } from "./icons/ContractIcon";
import { EngineIcon } from "./icons/EngineIcon";
import { InsightIcon } from "./icons/InsightIcon";
import { NebulaIcon } from "./icons/NebulaIcon";
import { PayIcon } from "./icons/PayIcon";
import { RPCIcon } from "./icons/RPCIcon";
import { SmartAccountIcon } from "./icons/SmartAccountIcon";
Expand Down
4 changes: 2 additions & 2 deletions apps/dashboard/src/app/(app)/(dashboard)/support/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { BookOpenIcon, ChevronRightIcon } from "lucide-react";
import { HomeIcon, WalletIcon } from "lucide-react";
import type { Metadata } from "next";
import Link from "next/link";
import { NebulaIcon } from "../(chain)/components/server/icons/NebulaIcon";
import { EngineIcon } from "../../(dashboard)/(chain)/components/server/icons/EngineIcon";
import { InsightIcon } from "../../(dashboard)/(chain)/components/server/icons/InsightIcon";
import { PayIcon } from "../../(dashboard)/(chain)/components/server/icons/PayIcon";
import { CustomChatButton } from "../../../nebula-app/(app)/components/CustomChat/CustomChatButton";
import { NebulaIcon } from "../../../nebula-app/(app)/icons/NebulaIcon";
import { CustomChatButton } from "../../../../components/CustomChat/CustomChatButton";
import {
getAuthToken,
getAuthTokenWalletAddress,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ClientOnly } from "components/ClientOnly/ClientOnly";
import { ClientOnly } from "@/components/blocks/client-only";
import type { Metadata } from "next";
import { UnixTimeConverter } from "./components/UnixTimeConverter";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ClientOnly } from "@/components/blocks/client-only";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { ClientOnly } from "components/ClientOnly/ClientOnly";
import { ContractTable } from "components/contract-components/tables/contract-table";
import { Suspense } from "react";
import type { ThirdwebClient } from "thirdweb";
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/app/(app)/login/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"use client";

import { getRawAccountAction } from "@/actions/getAccount";
import { ClientOnly } from "@/components/blocks/client-only";
import { GenericLoadingPage } from "@/components/blocks/skeletons/GenericLoadingPage";
import { ToggleThemeButton } from "@/components/color-mode-toggle";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { NEXT_PUBLIC_TURNSTILE_SITE_KEY } from "@/constants/public-envs";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { Turnstile } from "@marsidev/react-turnstile";
import { ClientOnly } from "components/ClientOnly/ClientOnly";
import { isVercel } from "lib/vercel-utils";
import { useTheme } from "next-themes";
import Link from "next/link";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AnnouncementBanner } from "components/notices/AnnouncementBanner";
import Link from "next/link";
import { redirect } from "next/navigation";
import { siwaExamplePrompts } from "../../../(dashboard)/support/page";
import { CustomChatButton } from "../../../../nebula-app/(app)/components/CustomChat/CustomChatButton";
import { CustomChatButton } from "../../../../../components/CustomChat/CustomChatButton";
import { getValidAccount } from "../../../account/settings/getAccount";
import {
getAuthToken,
Expand Down
Loading
Loading